Android平台外部编码数据实时预览播放SDK

Android平台除RTMP、RTSP直播播放外,有些场景可输出编码后(视频:H.264/H.265,音频:AAC/PCMA/PCMU)的数据,比如无人机或类似智能硬件设备,回调出来的H.264/H.265数据,编码后的H.264/H.265数据除了正常转推到RTMP、轻量级RTSP服务或GB28181外,还需要本地预览甚至对数据做二次处理(视频分析、实时水印字符叠加等),大概流程如下:

基于这样的场景诉求,我们开发了Android平台外部编码数据实时预览播放模块(以RTSP拉流,然后回调上来编码后数据,再投递到Android平台编码数据实时预览播放SDK为例)。

Android平台外部编码数据实时预览模块可实现本地低延迟的预览播放,支持软解码和特定机型硬解码,支持等比例或铺满显示,感兴趣的开发者可参看:Android平台如何实现第三方模块编码后(H.264/H.265/AAC/PCMA/PCMU)数据实时预览播放和播放

Android平台国网B接口接入SDK

电网视频监控系统是智能电网的一个重要组成部分,广泛应用于电网的建设、生产、运行、经营等方面。由于视频监控系统在不同的建设时期选用了不同的技术和不同厂家的产品,导致了标准不统一、技术路线不一致。目前国家电网公司智能电网建设,对视频监控系统提出了新的要求,因此实现统一监控、统一存储、分级控制、分域管理,使不同的视频监视系统能够互联互通,满足视频监控系统全局化、整体化的发展需求,已成为亟待解决的问题。

为此,大牛直播SDK推出的Android平台国网B接口接入SDK,可实现不具备国网B接口音视频能力的 Android终端,通过平台注册接入到现有的平台。

Android终端除支持常规的音视频数据接入外,还可以支持移动设备位置(MobilePosition)订阅和通知、语音广播,支持对接数据类型如下:

  1. 编码前数据(目前支持的有YV12/NV21/NV12/I420/RGB24/RGBA32/RGB565等数据类型),其中,Android平台前后摄像头数据,或者屏幕数据,或者Unity拿到的数据,均属编码前数据;
  2. 编码后数据(如无人机等264/HEVC数据,或者本地解析的MP4音视频数据);
  3. 拉取RTSP或RTMP流并接入至GB28181平台(比如其他IPC的RTSP流,可通过Android平台GB28181接入到国标平台)。

技术特点和优势:

  1. 全自研框架,易于扩展,自适应算法让延迟更低、采集编码传输效率更高;
  2. 所有功能以SDK接口形式提供,支持状态反馈;
  3. 可同时运行RTMP直播推送SDK、轻量级RTSP服务SDK和录像SDK;
  4. 支持外部YUV/RGB/H.264/H.265/AAC数据源接入;
  5. 所有参数均可通过SDK接口单独设置,亦可通过默认参数,傻瓜式设置。

功能支持:

  •  [视频格式]H.264/H.265(Android H.265硬编码);
  •  [音频格式]G.711 A律、AAC;
  •  [音量调节]Android平台采集端支持实时音量调节;
  •  [H.264硬编码]支持H.264特定机型硬编码;
  •  [H.265硬编码]支持H.265特定机型硬编码;
  •  [软硬编码参数配置]支持gop间隔、帧率、bit-rate设置;
  •  [软编码参数配置]支持软编码profile、软编码速度、可变码率设置;
  •  支持横屏、竖屏推流;
  •  Android平台支持后台service推送屏幕(推送屏幕需要5.0+版本);
  • 支持RTP OVER UDP和RTP OVER TCP被动模式;
  • 支持信令通道网络传输协议TCP/UDP设置;
  • 支持注册、注销,支持注册刷新及注册有效期设置;
  • 支持前端资源上报(Push_Resourse);
  • 支持请求获取资源(Request_Resource)应答;
  • 支持移动设备位置(MobilePosition)订阅和通知;
  • 支持语音广播;
  •  [实时水印]支持动态文字水印、png水印;
  •  [镜像]Android平台支持前置摄像头实时镜像功能;
  •  [实时静音]支持实时静音/取消静音;
  •  [实时快照]支持实时快照;
  •  [降噪]支持环境音、手机干扰等引起的噪音降噪处理、自动增益、VAD检测;
  •  [外部编码前视频数据对接]支持YUV数据对接;
  •  [外部编码前音频数据对接]支持PCM对接;
  •  [外部编码后视频数据对接]支持外部H.264数据对接;
  •  [外部编码后音频数据对接]外部AAC数据对接;
  •  [扩展录像功能]支持和录像SDK组合使用,录像相关功能。

对应Demo:

  •  Android工程:SmartPublisherV2、Camera2Demo

Android平台GB28181接入SDK

大牛直播SDK推出的Android平台GB28181接入SDK(SmartGBD),可实现不具备国标音视频能力的 Android终端,通过平台注册接入到现有的GB/T28181—2016服务,可用于如执法记录仪、智能安全帽、智能监控、智慧零售、智慧教育、远程办公、明厨亮灶、智慧交通、智慧工地、雪亮工程、平安乡村、生产运输、车载终端等场景,可能是业内为数不多功能齐全性能优异的商业级水准GB28181接入SDK。

Android终端除支持常规的音视频数据接入外,还可以支持移动设备位置(MobilePosition)订阅和通知、语音广播和语音对讲、历史视音频下载和回放,支持对接数据类型如下:

  1. 编码前数据(目前支持的有YV12/NV21/NV12/I420/RGB24/RGBA32/RGB565等数据类型),其中,Android平台前后摄像头数据,或者屏幕数据,或者Unity拿到的数据,均属编码前数据;
  2. 编码后数据(如无人机等264/HEVC数据,或者本地解析的MP4音视频数据);
  3. 拉取RTSP或RTMP流并接入至GB28181平台(比如其他IPC的RTSP流,可通过Android平台GB28181接入到国标平台)。

技术特点和优势:

  1. 全自研框架,易于扩展,自适应算法让延迟更低、采集编码传输效率更高;
  2. 所有功能以SDK接口形式提供,支持状态反馈;
  3. 可同时运行RTMP直播推送SDK、轻量级RTSP服务SDK和录像SDK;
  4. 支持外部YUV/RGB/H.264/H.265/AAC数据源接入;
  5. 所有参数均可通过SDK接口单独设置,亦可通过默认参数,傻瓜式设置。

功能支持:

  •  [视频格式]H.264/H.265(Android H.265硬编码);
  •  [音频格式]G.711 A律、AAC;
  •  [音量调节]Android平台采集端支持实时音量调节;
  •  [H.264硬编码]支持H.264特定机型硬编码;
  •  [H.265硬编码]支持H.265特定机型硬编码;
  •  [软硬编码参数配置]支持gop间隔、帧率、bit-rate设置;
  •  [软编码参数配置]支持软编码profile、软编码速度、可变码率设置;
  •  支持横屏、竖屏推流;
  •  Android平台支持后台service推送屏幕(推送屏幕需要5.0+版本);
  • 支持纯视频、音视频PS打包传输;
  • 支持RTP OVER UDP和RTP OVER TCP被动模式(TCP媒体流传输客户端);
  • 支持信令通道网络传输协议TCP/UDP设置;
  • 支持注册、注销,支持注册刷新及注册有效期设置;
  • 支持设备目录查询应答;
  • 支持心跳机制,支持心跳间隔、心跳检测次数设置;
  • 支持移动设备位置(MobilePosition)订阅和通知;
  •  适用国家标准:GB/T 28181—2016;
  • 支持语音广播;
  • 支持语音对讲;
  • 支持历史视音频文件检索;
  • 支持历史视音频文件下载;
  • 支持历史视音频文件回放;
  • 支持云台控制和预置位查询;
  •  [实时水印]支持动态文字水印、png水印;
  •  [镜像]Android平台支持前置摄像头实时镜像功能;
  •  [实时静音]支持实时静音/取消静音;
  •  [实时快照]支持实时快照;
  •  [降噪]支持环境音、手机干扰等引起的噪音降噪处理、自动增益、VAD检测;
  •  [外部编码前视频数据对接]支持YUV数据对接;
  •  [外部编码前音频数据对接]支持PCM对接;
  •  [外部编码后视频数据对接]支持外部H.264数据对接;
  •  [外部编码后音频数据对接]外部AAC数据对接;
  •  [扩展录像功能]支持和录像SDK组合使用,录像相关功能。

对应Demo:

  •  Android工程:SmartPublisherV2、Camera2Demo;

技术对接文档:

相关博客:

Unity环境下RTMP推流+RTMP播放低延迟解决方案

除了Windows/Linux/Android/iOS Native SDK,大牛直播SDK发布了Unity环境下的RTMP推流(Windows平台+Linux平台+Android平台)和RTMP|RTSP拉流(Windows、Linux、Android和iOS平台全覆盖)低延迟的解决方案。

目前,大牛直播SDK的Unity3D环境下,已覆盖以下SDK:

  •  Windows平台RTMP直播推送SDK(采集Unity窗体、摄像头或屏幕);
  •  Windows平台RTMP|RTSP直播播放SDK;
  •  Linux平台RTMP直播推送SDK(采集Unity窗体、Unity声音);
  •  Linux平台RTMP|RTSP直播播放SDK;
  •  Android平台RTMP直播推送SDK(采集Unity窗体、麦克风或Unity声音);
  •  Android平台RTMP|RTSP直播播放SDK;
  •  iOS平台RTMP|RTSP直播播放SDK。

1. Unity环境下RTMP推流

Unity环境下,不管是camera还是窗体数据也好,主要是高效率的拿到原始数据,采集端可用的数据格式是RGB的,拿到之后,通过高效率的数据传递,发给封装后的原生SDK,完成数据编码和RTMP推送。

需要注意的地方有几点:

1. 数据采集投递,确保高效率;

2. 屏幕分辨率发生变化,可实时适配;

3. Unity和原生SDK之间通信,比如event回调等;

4. 屏幕数据如有水平或垂直翻转,需要有一定的矫正。

2. Unity环境下RTMP|RTSP播放器

Unity环境下RTMP或RTSP直播播放我们前几年就有发布,并已应用在好多传统行业领域,比如教育或工业仿真或一些低延迟的控制场景。

相关实现逻辑如下:

1. Native RTSP或RTSP直播播放SDK回调RGB/YUV420/NV12等其中的一种未压缩的图像格式;

2. Unity3D创建相应的RGB/YUV420等Shader;

3.Unity3D从各个平台获取图像数据来填充纹理即可;

需要注意的有几点:

1. 多实例支持:播放端和推送不一样,比如智慧城市,播放端有多路场景,所以多实例支持是必备功能,多实例环境下,需要能有好的区分event状态回调等;

2. 尽可能高效率的数据传递,确保资源占有最小化;

3. 视频分辨率变化后,能自动适配;

4. Unity和原生SDK之间通信,比如event回调等;

5. 长时间运行稳定性。

相关SDK文档及视频

大牛直播SDK Unity3D接口调用SDK说明

Unity3d RTSP/RTMP直播播放端SDK视频演示1

Unity3d RTSP/RTMP直播播放端SDK视频演示2

相关博客

Windows平台Unity3d下如何同时播放多路RTSP或RTMP流

如何在Unity3d平台下低延迟播放RTMP或RTSP流

Windows平台实现Unity下窗体|摄像头|屏幕采集推送

Android平台实现Unity3D下RTMP推送

Unity3D平台实现全景实时RTMP|RTSP流渲染

Unity3D下Linux平台播放RTSP或RTMP流

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

Unity实现Camera和Audio数据的低延迟RTMP推送技术探讨

Windows平台实现Unity下窗体|摄像头|屏幕采集推送

技术背景

随着Unity3D的应用范围越来越广,越来越多的行业开始基于Unity3D开发产品,如传统行业中虚拟仿真教育、航空工业、室内设计、城市规划、工业仿真等领域。

基于此,好多开发者苦于在Unity环境下,没有低延迟的推拉流解决方案,前几年,我们在Unity环境下推出了跨平台低延迟的RTMP|RTSP直播播放器,很好的解决了好多对延迟要求苛刻的使用场景。

随着时间的推移,越来越多的开发者联系我们,希望我们能推出Unity环境下的RTMP推送模块,获取到unity的实时数据,更低延迟更高效率的实现数据传输推送,基于此,我们发布了Unity环境下的RTMP推送模块。

本文以Windows平台为例,数据源分别为Unity的窗口、摄像头或整个屏幕,编码传输模块,还是调用大牛直播SDK(官方)的原生接口,简单界面先睹为快:

技术实现

1. 基础初始化


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

2. 调用Open()接口,获取推送实例

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

3. 初始化参数配置

这里需要注意下,如果要采集unity窗口,需要设置图层模式,先填充一层RGBA黑色背景,然后再添加一层,用于叠加外部数据。

       private void SetCommonOptionToPublisherSDK()
        {
            if (!IsPublisherHandleAvailable())
            {
                Debug.Log("SetCommonOptionToPublisherSDK, publisher handle with null..");
                return;
            }

            NTSmartPublisherSDK.NT_PB_ClearLayersConfig(publisher_handle_, 0,
                            0, IntPtr.Zero);

            if (video_option == NTSmartPublisherDefine.NT_PB_E_VIDEO_OPTION.NT_PB_E_VIDEO_OPTION_LAYER)
            {
                // 第0层填充RGBA矩形, 目的是保证帧率, 颜色就填充全黑
                int red = 0;
                int green = 0;
                int blue = 0;
                int alpha = 255;

                NT_PB_RGBARectangleLayerConfig rgba_layer_c0 = new NT_PB_RGBARectangleLayerConfig();

                rgba_layer_c0.base_.type_ = (Int32)NTSmartPublisherDefine.NT_PB_E_LAYER_TYPE.NT_PB_E_LAYER_TYPE_RGBA_RECTANGLE;
                rgba_layer_c0.base_.index_ = 0;
                rgba_layer_c0.base_.enable_ = 1;
                rgba_layer_c0.base_.region_.x_ = 0;
                rgba_layer_c0.base_.region_.y_ = 0;
                rgba_layer_c0.base_.region_.width_ = video_width_;
                rgba_layer_c0.base_.region_.height_ = video_height_;

                rgba_layer_c0.base_.offset_ = Marshal.OffsetOf(rgba_layer_c0.GetType(), "base_").ToInt32();
                rgba_layer_c0.base_.cb_size_ = (uint)Marshal.SizeOf(rgba_layer_c0);

                rgba_layer_c0.red_ = System.BitConverter.GetBytes(red)[0];
                rgba_layer_c0.green_ = System.BitConverter.GetBytes(green)[0];
                rgba_layer_c0.blue_ = System.BitConverter.GetBytes(blue)[0];
                rgba_layer_c0.alpha_ = System.BitConverter.GetBytes(alpha)[0];

                IntPtr rgba_conf = Marshal.AllocHGlobal(Marshal.SizeOf(rgba_layer_c0));

                Marshal.StructureToPtr(rgba_layer_c0, rgba_conf, true);

                UInt32 rgba_r = NTSmartPublisherSDK.NT_PB_AddLayerConfig(publisher_handle_, 0,
                                rgba_conf, (int)NTSmartPublisherDefine.NT_PB_E_LAYER_TYPE.NT_PB_E_LAYER_TYPE_RGBA_RECTANGLE,
                                0, IntPtr.Zero);

                Marshal.FreeHGlobal(rgba_conf);

                NT_PB_ExternalVideoFrameLayerConfig external_layer_c1 = new NT_PB_ExternalVideoFrameLayerConfig();

                external_layer_c1.base_.type_ = (Int32)NTSmartPublisherDefine.NT_PB_E_LAYER_TYPE.NT_PB_E_LAYER_TYPE_EXTERNAL_VIDEO_FRAME;
                external_layer_c1.base_.index_ = 1;
                external_layer_c1.base_.enable_ = 1;
                external_layer_c1.base_.region_.x_ = 0;
                external_layer_c1.base_.region_.y_ = 0;
                external_layer_c1.base_.region_.width_ = video_width_;
                external_layer_c1.base_.region_.height_ = video_height_;

                external_layer_c1.base_.offset_ = Marshal.OffsetOf(external_layer_c1.GetType(), "base_").ToInt32();
                external_layer_c1.base_.cb_size_ = (uint)Marshal.SizeOf(external_layer_c1);

                IntPtr external_layer_conf = Marshal.AllocHGlobal(Marshal.SizeOf(external_layer_c1));

                Marshal.StructureToPtr(external_layer_c1, external_layer_conf, true);

                UInt32 external_r = NTSmartPublisherSDK.NT_PB_AddLayerConfig(publisher_handle_, 0,
                                external_layer_conf, (int)NTSmartPublisherDefine.NT_PB_E_LAYER_TYPE.NT_PB_E_LAYER_TYPE_EXTERNAL_VIDEO_FRAME,
                                0, IntPtr.Zero);

                Marshal.FreeHGlobal(external_layer_conf);

            }
            else if (video_option == NTSmartPublisherDefine.NT_PB_E_VIDEO_OPTION.NT_PB_E_VIDEO_OPTION_CAMERA)
            {
                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_, video_width_, video_height_));

            Int32 type = 0;   //软编码
            Int32 encoder_id = 1;
            UInt32 codec_id = (UInt32)NTCommonMediaDefine.NT_MEDIA_CODEC_ID.NT_MEDIA_CODEC_ID_H264;
            Int32 param1 = 0;

            SetVideoEncoder(type, encoder_id, codec_id, param1);

            SetVideoQualityV2(CalVideoQuality(video_width_, video_height_, is_h264_encoder));

            SetVideoMaxBitRate((CalMaxKBitRate(edit_key_frame_, video_width_, video_height_, false)));

            SetVideoKeyFrameInterval((edit_key_frame_));

            if (is_h264_encoder)
            {
                SetVideoEncoderProfile(1);
            }

            SetVideoEncoderSpeed(CalVideoEncoderSpeed(video_width_, video_height_, is_h264_encoder));

            // 音频相关设置

            SetAuidoInputDeviceId(0);
            SetPublisherAudioCodecType(1);
            SetPublisherMute(is_mute);
            SetEchoCancellation(0, 0);
            SetNoiseSuppression(0);
            SetAGC(0);
            SetVAD(0);
            SetInputAudioVolume(Convert.ToSingle(edit_audio_input_volume_));
        }

4. 数据采集

摄像头和屏幕的数据采集,还是调用原生的SDK接口,本文不再赘述,如果需要采集Unity窗体的数据,可以用参考以下代码:

        if ( texture_ == null || video_width_ != Screen.width || video_height_ != Screen.height)
        {
            Debug.Log("OnPostRender screen changed++ scr_width: " + Screen.width + " scr_height: " + Screen.height);

            if (screen_image_ != IntPtr.Zero)
            {
                Marshal.FreeHGlobal(screen_image_);
                screen_image_ = IntPtr.Zero;
            }

            if (texture_ !=  null)
            {
                UnityEngine.Object.Destroy(texture_);
                texture_ = null;
            }

            video_width_ = Screen.width;
            video_height_ = Screen.height;

            texture_ = new Texture2D(video_width_, video_height_, TextureFormat.BGRA32, false);

            screen_image_ = Marshal.AllocHGlobal(video_width_ * 4 * video_height_);

            Debug.Log("OnPostRender screen changed--");

            return;
        }

        texture_.ReadPixels(new Rect(0, 0, video_width_, video_height_), 0, 0, false);
        texture_.Apply();

从 texture里面,通过调用 GetRawTextureData(),获取到原始数据。

5. 数据对接

数据对接,通过调用以下接口:

       public void OnPostRGBAData(IntPtr data, int length, int stride, int width, int height)
        {
            NT_PB_Image pb_image = new NT_PB_Image();
            
            pb_image.format_ = (int)NTSmartPublisherDefine.NT_PB_E_IMAGE_FORMAT.NT_PB_E_IMAGE_FORMAT_RGB32;
            pb_image.width_ = width;
            pb_image.height_ = height;
            pb_image.timestamp_ = 0;
            pb_image.cb_size_ = (UInt32)Marshal.SizeOf(pb_image);   

            pb_image.stride_ = new Int32[16];
            pb_image.stride_[0] = stride;

            pb_image.plane_size_ = new Int32[16];
            pb_image.plane_size_[0] = pb_image.stride_[0] * pb_image.height_;         

            pb_image.plane_ = new IntPtr[16];
            pb_image.plane_[0] = data;

            IntPtr image_data = Marshal.AllocHGlobal(Marshal.SizeOf(pb_image));

            Marshal.StructureToPtr(pb_image, image_data, true);

            NTSmartPublisherSDK.NT_PB_PostLayerImage(publisher_handle_, 0,
                            1, image_data, 0, IntPtr.Zero);

            Marshal.FreeHGlobal(image_data);
        }

6. 本地数据预览

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

设置preview后,处理preview的数据回调

        //预览数据回调
        public void SDKVideoPreviewImageCallBack(IntPtr handle, IntPtr user_data, IntPtr image)
        {
            NT_PB_Image pb_image = (NT_PB_Image)Marshal.PtrToStructure(image, typeof(NT_PB_Image));

            NT_VideoFrame pVideoFrame = new NT_VideoFrame();

            pVideoFrame.width_ = pb_image.width_;
            pVideoFrame.height_ = pb_image.height_;

            pVideoFrame.stride_ = pb_image.stride_[0];

            Int32 argb_size = pb_image.stride_[0] * pb_image.height_;

            pVideoFrame.plane_data_ = new byte[argb_size];
            
            if (argb_size > 0)
            {
                Marshal.Copy(pb_image.plane_[0],pVideoFrame.plane_data_,0, argb_size);
            }

            {
                cur_image_ = pVideoFrame;
            }
        }      

7. 相关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);
        }

8. 开始推送、停止推送

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

9. 关闭实例

        public void Close()
        {
            if (0 == publisher_handle_count_)
            {
                NTSmartPublisherSDK.NT_PB_Close(publisher_handle_);
                publisher_handle_ = IntPtr.Zero;
            }
        }

总结

经测试,Unity环境下,通过高效率的数据采集、编码和推送,配合SmartPlayer播放器播放,整体延迟可控制在毫秒级,可适用于大多数Unity环境下对延迟和稳定性要求苛刻的场景。

Unity3D RTMP直播推流SDK

好多开发者苦于很难在unity3d下实现RTMP直播推送,本次以大牛直播SDK的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播放端,依然可以实现毫秒级延迟的推拉流体验。

大牛直播SDK试用、测试服务协议

欢迎使用上海视沃信息科技有限公司(以下简称“视沃科技”)旗下“大牛直播SDK”,试用测试前,请您仔细阅读视沃科技官方网站公布的相关规范和使用流程及本协议的全部内容,如您不同意前述任意内容,请不要进行后续操作。如您实际使用了“大牛直播SDK”,我方将视为您已完全理解并认同规范、流程及服务协议的全部内容。

  • 协议主体

本服务协议是因您使用大牛直播SDK与视沃科技所订立的有效合约。

  • 协议的订立与生效

一旦您选择试用、测试大牛直播SDK并进行后续操作,即表示您同意遵循本服务协议之所有约定,本协议即成为双方之间就大牛直播SDK软件包服务达成的有效合约。

  • 大牛直播SDK软件包服务的使用

3.1 在试用测试大牛直播SDK软件包服务前,您应知悉阅读视沃科技官网页面上的相关规范、使用流程,并理解相关内容及可能发生的后果,在使用大牛直播SDK软件包服务的过程中,您应依照相关操作指引进行操作,请您自行把握风险谨慎操作。

3.2您理解并同意,使用大牛直播SDK软件服务包是您自行独立审慎判断的结果,您将自行对此负责,包括但不限于:

3.2.1在使用过程中,您将对自行操作的行为及产生的结果负责;

3.2.1在试用、测试大牛直播SDK软件服务包阶段可免费使用,但正式授权版需签订购买合同并支付相关费用;

3.2.3在使用大牛直播SDK软件包过程中,您不应进行任何破坏或试图破坏网络安全的行为,您承诺SDK试用、测试及授权后的使用场景保持一致且视频内容合法合规,不得含有我国法律、行政法规禁止发布或传输的信息,为履行法律赋予的安全管理义务,请您下载填写附件1并将签章后的附件1发至指定邮箱;

3.2.4除视沃科技明示许可外,不得修改、翻译、改编、转许可、转让大牛直播SDK软件服务包,也不得逆向工程、反编译或试图以其他方式发现大牛直播SDK软件服务包或软件源代码;

3.2.5您不应以任何将违反国家、地方法律法规、行业管理和社会公共道德、及影响、损害或可能影响、损害视沃科技利益的方式或目的使用大牛直播SDK。

  • 责任限制

您理解并同意,在免费测试、试用其间,视沃科技虽然对大牛直播SDK软件包服务提供可用性支撑,但不对其中任何错误或漏洞提供任何担保,并不对您使用大牛直播SDK软件服务包的工作和结果承担任何责任。

  • 变更和终止

5.1 您理解并认可,视沃科技保留随时修改、取消、增强大牛直播SDK软件包服务一项或多项功能的权利;

5.2如您有任何违反本服务协议的情形,或根据视沃科技自己的独立判断认为您对大牛直播SDK的使用行为不符合我司要求,我司有权随时中断您使用大牛直播SDK而无需通知您,并将相关情况向有关主管部门报告;同时,如给我司造成损失,我司有权要求赔偿。

  • 保密

您及视沃科技都应对对方的保密信息承担保密责任,除非经国家行政、司法等有权机关要求披露或该信息已进入公有领域。

  • 其他

7.1视沃科技有权随时根据有关法律、法规的变化及公司经营状况和经营策略的调整等修改本服务协议。修改后的服务协议会在视沃科技官网公布。如果不同意修改的内容,您应停止使用大牛直播SDK软件包服务。如果继续使用大牛直播SDK软件包,则视为您接受本服务协议的变动。

7.2如果本服务协议中的任何条款无论因何种原因完全或部分无效或不具有执行力,或违反任何适用的法律,则该条款被视为删除,但本服务协议的其余条款仍应有效并且有约束力。

7.3本服务协议受中华人民共和国法律管辖。在执行本服务协议过程中如发生纠纷,双方应及时协商解决。协商不成时,任何一方可直接向上海市长宁区人民法院提起诉讼。

附件1:

  • 甲方(测试、试用方公司名称):
  • 测试、试用大牛直播SDK实际使用场景描述:(附文字说明、产品截图、视频画面)
  • 在使用大牛直播SDK软件包过程中,您不应进行任何破坏或试图破坏网络安全的行为,您承诺SDK试用、测试及授权后的使用场景保持一致且视频内容合法合规,不得含有我国法律、行政法规禁止发布或传输的信息

附件1请发送至 1130758427@qq.com

 

甲方签章

xxxx年xx月xx日

 

视沃科技(大牛直播SDK)官方测试版获取流程

视沃科技官方测试版本暂不提供网络下载,如因产品需求,需要测试版,请按照以下流程:

  1. 联系视沃科技官方商务|技术人员电话或QQ;手机:130-7210-2209 或 135-6452-9354  QQ:89030985  或 517631076
  2. 查看“大牛直播SDK试用、测试服务协议.pdf”;右键另存为
  3. 填写“上海视沃信息科技有限公司SDK使用场景调查表”,并签章(如公司签章流程复杂,可个人签字、留联系方式,并附带工牌等可证明公司身份的材料);右键另存为
  4. 公司审核通过后,获取试用版和前期技术支持。

Windows平台RTSP|RTMP播放端SDK集成说明

2.1 demo说明

  • 大牛直播SDK提供C++/C#两套接口,对外提供32/64位debug/release库,C++和C#接口一一对应,C#接口比C++接口增加前缀NT_PB_;
  • WIN-PlayerSDK-CPP-Demo:播放端SDK对应的C++接口的demo;
  • WIN-PlayerSDK-CSharp-Demo:播放端SDK对应的C#接口的demo;
  • 播放端SDK支持Win7及以上系统;
  • 本demo基于VS2013开发。

2.2 界面UI展示

2.3集成说明

C++头文件:

  • [类型定义]nt_type_define.h
  • [Log定义]smart_log.h
  • [Log定义]smart_log_define.h
  • [base code定义]nt_base_code_define.h
  • [player接口]smart_player_define.h
  • [player参数定义]smart_player_sdk.h

C#头文件:

  • [base code定义]nt_base_code_define.cs
  • [player接口]smart_player_define.cs
  • [player参数定义]smart_player_sdk.cs

相关Lib:

  • SmartLog.dll
  • SmartLog.lib
  • SmartPlayerSDK.dll
  • SmartPlayerSDK.lib
  • avcodec-56.dll
  • avdevice-56.dll
  • avfilter-5.dll
  • avformat-56.dll
  • avutil-54.dll
  • postproc-53.dll
  • swresample-1.dll
  • swscale-3.dll

集成步骤:

  1. 把lib目录下debug/release库拷贝到需要集成的工程对应的debug或release目录下(确保32位/64位库debug/release目录一一对应);

lib目录如下:

    1. 32位debug库:debug
    2. 32位release库:release
    3. 64位debug库:x64\debug
    4. 64位release库:x64\release

2. 相关cs头文件,加入需要集成的工程;

3. 在需要集成的工程,右键->Properties->

Application->Assembly name,大牛直播SDK按照APP名称授权,未授权版本,此处请改成“SmartPlayer”,如需授权,可直接联系商务;

4. 正式授权版,需要在Init()接口调用之前添加设置license的代码(相关Key和CID请根据正式授权版邮件说明填写):

2.4 接口调用时序(以C#为例)

2.4.1 设置授权license

C#的SDK,请在在NT.NTSmartPlayerSDK.NT_SP_Init之前添加下面的代码:

NT.NTSmartPlayerSDK.NT_SP_SetSDKClientKey("xxxxxxxxxx", "xxxxxxxxxx", 0, IntPtr.Zero);

UInt32 isInited = NT.NTSmartPlayerSDK.NT_SP_Init(0, IntPtr.Zero);
if (isInited != 0)
{
    MessageBox.Show("调用NT_SP_Init失败..");
    return;
}

C++的SDK,请在player_api_.Init之前添加下面的代码:

NT_SP_SetSDKClientKey(NT_SP_SetSDKClientKey("xxxxxxxxxx", "xxxxxxxxxx", 0, nullptr);

if ( NT_ERC_OK != player_api_.Init(0, NULL) )
{
    return FALSE;
}

2.4.2 设置日志存放路径

需要在player_api_.Init之前添加下面的代码:

// 设置日志路径(请确保目录存在)
String log_path = "D:\\playerlog";
NTSmartLog.NT_SL_SetPath(log_path);

如目录存在,并具备文件写入权限,关闭应用程序后,相关文件夹下会有smart_sdk.log生成。

2.4.3 初始化SDK

NT_SP_Init:SDK初始化,多实例播放,此接口仅需调用一次即可。

2.4.4 特定机型硬解码检测

如系统用于特定机型环境下,特别是多路播放场景,需用到硬解码的话,可以用以下两组接口检测系统是否支持硬解。

注:在软解性能满足系统需求的前提下,一般建议优先使用软解。

/*
 * 检查是否支持H264硬解码
 * 如果支持的话返回NT_ERC_OK
 */
[DllImport(@"SmartPlayerSDK.dll")]
public static extern UInt32 NT_SP_IsSupportH264HardwareDecoder();

/*
  * 检查是否支持H265硬解码
  * 如果支持的话返回NT_ERC_OK
  */
[DllImport(@"SmartPlayerSDK.dll")]
public static extern UInt32 NT_SP_IsSupportH265HardwareDecoder();

如需使用硬解码,调用如下接口即可:

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

2.4.5 Open生成播放实例

NT_SP_Open:每调用一次Open接口,对应一个播放实例,如需播放多实例,对应多个player handler。

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

2.4.6 设置回调事件

  1. NT_SP_SetEventCallBack:用于回调网络链接状态、buffer状态(开始、buffer比例、结束)、实时带宽等,对应EventID如下:
/*事件ID*/
public enum NT_SP_E_EVENT_ID : uint
{
        NT_SP_E_EVENT_ID_BASE = NTBaseCodeDefine.NT_EVENT_ID_SMART_PLAYER_SDK,

        NT_SP_E_EVENT_ID_CONNECTING          = NT_SP_E_EVENT_ID_BASE | 0x2, /*连接中*/
        NT_SP_E_EVENT_ID_CONNECTION_FAILED = NT_SP_E_EVENT_ID_BASE | 0x3, /*连接失败*/
        NT_SP_E_EVENT_ID_CONNECTED       = NT_SP_E_EVENT_ID_BASE | 0x4, /*已连接*/
        NT_SP_E_EVENT_ID_DISCONNECTED     = NT_SP_E_EVENT_ID_BASE | 0x5, /*断开连接*/
        NT_SP_E_EVENT_ID_NO_MEDIADATA_RECEIVED = NT_SP_E_EVENT_ID_BASE | 0x8,  /*收不到RTMP数据*/
        NT_SP_E_EVENT_ID_RTSP_STATUS_CODE   = NT_SP_E_EVENT_ID_BASE | 0xB,  /*rtsp status code上报, 目前只上报401, param1表示status code*/

        /* 接下来请从0x81开始*/
        NT_SP_E_EVENT_ID_START_BUFFERING = NT_SP_E_EVENT_ID_BASE | 0x81, /*开始缓冲*/
        NT_SP_E_EVENT_ID_BUFFERING     = NT_SP_E_EVENT_ID_BASE | 0x82, /*缓冲中, param1 表示百分比进度*/
        NT_SP_E_EVENT_ID_STOP_BUFFERING  = NT_SP_E_EVENT_ID_BASE | 0x83, /*停止缓冲*/

        NT_SP_E_EVENT_ID_DOWNLOAD_SPEED  = NT_SP_E_EVENT_ID_BASE | 0x91, /*下载速度, param1表示下载速度,单位是(Byte/s)*/

        NT_SP_E_EVENT_ID_PLAYBACK_REACH_EOS = NT_SP_E_EVENT_ID_BASE | 0xa1,     /*播放结束, 直播流没有这个事件,点播流才有*/
        NT_SP_E_EVENT_ID_RECORDER_REACH_EOS = NT_SP_E_EVENT_ID_BASE | 0xa2,     /*录像结束, 直播流没有这个事件, 点播流才有*/
        NT_SP_E_EVENT_ID_PULLSTREAM_REACH_EOS = NT_SP_E_EVENT_ID_BASE | 0xa3,   /*拉流结束, 直播流没有这个事件,点播流才有*/

        NT_SP_E_EVENT_ID_DURATION = NT_SP_E_EVENT_ID_BASE | 0xa8, /*视频时长,如果是直播,则不上报,如果是点播的话, 若能从视频源获取视频时长的话,则上报, param1表示视频时长,单位是毫秒(ms)*/
}

  1. NT_SP_SetVideoSizeCallBack:设置视频分辨率回调,如流数据携带视频数据,SDK会回上来视频宽高信息:
//video resolution callback
video_size_call_back_ = new SP_SDKVideoSizeCallBack(SP_SDKVideoSizeHandle);
NTSmartPlayerSDK.NT_SP_SetVideoSizeCallBack(player_handle_, IntPtr.Zero, video_size_call_back_);

注意:视频宽高回上来或绘制窗口发生变化时,记得调用NT_SP_OnWindowSize()更新,如不调用可能会引起视频模糊。

private void PlaybackWindowResized(Int32 width,Int32 height)
{
    width_=width;
    height_=height;

    int left=playWnd.Left;
    int top=playWnd.Top;

    textBox_resolution.Text=width+"*"+height;

    if(player_handle_==IntPtr.Zero)
    {
       return;
    }

    NTSmartPlayerSDK.NT_SP_OnWindowSize(player_handle_,playWnd.Width,playWnd.Height);
}
  1. NT_SP_SetVideoFrameCallBack:设置YUV/RGB32数据回调,可用于对接第三方视频分析,或自行绘制等,如系统不支持D3D绘制,可设置回调数据,上层GDI模式绘制:
/*定义视频帧图像格式*/
public enum NT_SP_E_VIDEO_FRAME_FORMAT : uint
{
      NT_SP_E_VIDEO_FRAME_FORMAT_RGB32 = 1, // 32位的rgb格式, r, g, b各占8, 另外一个字节保留, 内存字节格式为: bb gg rr xx, 主要是和windows位图匹配, 在小端模式下,按DWORD类型操作,最高位是xx, 依次是rr, gg, bb
      NT_SP_E_VIDEO_FRAME_FORMAT_ARGB = 2, // 32位的argb格式,内存字节格式是: bb gg rr aa 这种类型,和windows位图匹配
      NT_SP_E_VIDEO_FRAME_FROMAT_I420 = 3, // YUV420格式, 三个分量保存在三个面上
}

  1. NT_SP_SetVideoFrameCallBackV2:设置YUV/RGB32数据回调,与NT_SP_SetVideoFrameCallBack接口的不同在于,吐出来的视频数据, 可以指定宽高;
  2. NT_SP_SetRenderVideoFrameTimestampCallBack:设置绘制视频帧时,视频帧时间戳回调,一般播放器无时间戳回调需求的话,无需设置:
//video timestamp callback
video_frame_ts_callback_ = new SP_SDKRenderVideoFrameTimestampCallBack(SP_SDKRenderVideoFrameTimestampCallBack);
NTSmartPlayerSDK.NT_SP_SetRenderVideoFrameTimestampCallBack(player_handle_, IntPtr.Zero, video_frame_ts_callback_);
  1. NT_SP_SetAudioPCMFrameCallBack:设置音频PCM帧回调, 吐PCM数据出来,目前每帧大小是10ms,一般播放器无使用需求的话,无需设置;
  1. NT_SP_SetUserDataCallBack:设置用户数据回调,此接口需要和推送端SDK配套使用,用于返回推送端设定的实时用户数据(如时间戳、经纬度等各种扩展指令或信息),如只是单纯使用播放SDK,无需设置;
  1. NT_SP_SetSEIDataCallBack:设置视频SEI数据回调,如只是单纯使用播放SDK,不需要额外处理扩展SEI数据的话,无需设置。

2.4.7 D3DRender检测

目前,几乎很少存在不支持D3D绘制的情况,考虑到系统通用性,我们在播放之前,先做检测,具体调用接口如下:

/*
 * handle: 播放句柄
 * hwnd: 这个要传入真正用来绘制的窗口句柄
 * is_support: 如果支持的话 *is_support 为1, 不支持的话为0
 * 接口调用成功返回NT_ERC_OK
 */
[DllImport(@"SmartPlayerSDK.dll")]
public static extern UInt32 NT_SP_IsSupportD3DRender(IntPtr handle, IntPtr hwnd, ref Int32 is_support);

对于不支持D3D绘制的情况下,设置回调YUV数据,上层直接用GDI模式绘制,注意:GDI绘制效率偏低。

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

2.4.8 设置播放URL

NT_SP_SetURL:支持rtsp/rtmp/本地FLV文件(全路径)。

2.4.9 设置回调PCM

NT_SP_SetIsOutputAudioDevice:设置是否播放出声音,这个和静音接口是有区别的,这个接口的主要目的是为了用户设置了外部PCM回调接口后,又不想让SDK播放出声音时使用。

2.4.10 RTMP/RTSP播放参数设置

具体可参照Demo源码里面InitCommonSDKParam():

2.4.10.1 播放前可选设置接口

  1. NT_SP_SetBuffer:设置视频播放缓冲buffer大小,单位:毫秒;
  2. NT_SP_SetRTSPTcpMode:设置RTSP TCP 模式, 1为TCP, 0为UDP, 此接口仅RTSP有效;
  3. NT_SP_SetRtspTimeout:设置RTSP超时时间, timeout单位为秒,必须大于0;
  4. NT_SP_SetRtspAutoSwitchTcpUdp:对于RTSP来说,有些可能支持rtp over udp方式,有些可能支持使用rtp over tcp方式. 为了方便使用,有些场景下可以开启自动尝试切换开关, 打开后如果udp无法播放,sdk会自动尝试tcp, 如果tcp方式播放不了,sdk会自动尝试udp, is_auto_switch_tcp_udp: 如果设置1的话, sdk将在tcp和udp之间尝试切换播放,如果设置为0,则不尝试切换;
  5. NT_SP_SetFastStartup:设置秒开, 1为秒开, 0为不秒开,此接口用于如RTMP服务器缓存GOP时,酌情使用;
  6. NT_SP_SetLowLatencyMode:设置低延时播放模式,默认是正常播放模式,mode: 1为低延时模式, 0为正常模式,低延迟模式下,可能会导致音视频不同步,或视频帧不均匀;
  7. NT_SP_SetReportDownloadSpeed:设置下载速度上报, 默认不上报下载速度;

* is_report: 上报开关, 1: 表上报. 0: 表示不上报. 其他值无效.

* report_interval: 上报时间间隔(上报频率),单位是秒,最小值是1秒1次. 如果小于1且设置了上报,将调用失败

* 注意:如果设置上报的话,请设置SetEventCallBack, 然后在回调函数里面处理这个事件.

* 上报事件是:NT_SP_E_EVENT_ID_DOWNLOAD_SPEED

  1. NT_SP_GetDownloadSpeed:主动获取下载速度,speed: 返回下载速度,单位是Byte/s;
  2. NT_SP_SetParam:万能接口, 设置参数, 大多数问题, 这些接口都能解决;
  3. NT_SP_GetParam:万能接口, 得到参数, 大多数问题,这些接口都能解决;

2.4.10.2 播放前后可实时调用的接口

  1. NT_SP_SetMute:播放过程中,实时静音、取消静音,可播放之前调用,亦或播放过程中实时调用;
  2. NT_SP_SetAudioVolume:不同于实时静音接口,此接口可以更细粒度的控制音量,默认范围[0,100],其中0是静音,100是最大音量, 默认是100;
  3. NT_SP_SetOnlyDecodeVideoKeyFrame:多窗口播放场景下,部分窗口可能只需要播放关键帧,如有类似场景需求,可用此接口;
  4. NT_SP_SetRotation:设置视频View旋转,顺时针旋转,degress: 设置0, 90, 180, 270度有效,其他值无效,注意:除了0度,其他角度播放会耗费更多CPU;
  5. NT_SP_SetFlipVertical:设置视频View上下反转(垂直反转);
  6. NT_SP_SetFlipHorizontal:设置视频View水平反转;
  7. NT_SP_SetRenderScaleMode:设置视频画面的填充模式,如填充整个绘制窗口、等比例填充绘制窗口,如不设置,默认填充整个绘制窗口;

2.4.11 开始播放

NT_SP_StartPlay

开始播放RTMP或RTSP流数据。

2.4.12 RTMP/RTSP拉流端录像

  1. NT_SP_SetRecorderDirectory:设置录像目录
  2. NT_SP_SetRecorderFileMaxSize:设置单个文件最大大小
  3. NT_SP_SetRecorderFileNameRuler:设置录像文件名生成规则
  4. NT_SP_SetRecorderCallBack:设置录像回调接口
  5. NT_SP_SetRecorderAudioTranscodeAAC:设置录像时音频转AAC编码的开关, aac比较通用,sdk增加其他音频编码(比如speex, pcmu, pcma等)转aac的功能
  6. NT_SP_SetRecorderVideo:设置是否录视频,默认的话,如果视频源有视频就录,没有就没得录, 但有些场景下可能不想录制视频,只想录音频,所以增加个开关
  7. NT_SP_SetRecorderAudio:设置是否录音频,默认的话,如果视频源有音频就录,没有就没得录, 但有些场景下可能不想录制音频,只想录视频,所以增加个开关
  8. NT_SP_StartRecorder:启动录像
  9. NT_SP_StopRecorder:停止录像

2.4.13 实时快照

NT_SP_CaptureImage

用于播放端实时截取当前播放图片,图片以PNG形式保存至本地。

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
{
    // 其他失败
}

2.4.14 快速切换URL

NT_SP_SwitchURL

快速切换URL,用于不用析构整个player实例的前提下,实时切换播放的URL。

2.4.15 用户数据回调

NT_SP_SetUserDataCallBack

设置用户数据回调,用于接收扩展SEI模块发送的用户数据信息,如不是配合我们扩展SEI发送DK,此接口无需调用。

2.4.16 SEI数据回调

NT_SP_SetSEIDataCallBack

设置视频sei数据回调,用于接收SEI数据回调,如流数据不存在SEI或不准备处理SEI数据,此接口无需调用。

2.4.17 停止播放

NT_SP_StopPlay

停止播放RTMP或RTSP流数据。

2.4.18 关闭播放实例

NT_SP_Close

调用Close接口后,player handler置空。

if ( player_handle_ != IntPtr.Zero)
{
     NTSmartPlayerSDK.NT_SP_Close(player_handle_);
     player_handle_ = IntPtr.Zero;
}

2.4.19 Uninit

NT_SP_UnInit

UnInit() 是SDK最后一个调用的接口,多实例环境下,只需要调用一次即可。

Windows平台RTMP/RTSP直播推送模块设计和使用说明

开发背景

好多开发者一直反馈,Windows平台,做个推屏或者推摄像头,推RTMP或者RTSP出去,不知道哪些功能是必须的,哪些设计是可有可无的,还有就是,不知道如何选技术方案,以下是基于我们设计的Windows平台RTSP、RTMP直播推送模块,设计和使用说明,供大家参考。

整体方案架构

Windows平台RTMP或RTSP推送,系采集端模块,主要完成,屏幕或者摄像头数据、麦克风或扬声器数据的采集,编码,然后按照特定格式打包,通过RTMP或者RTSP传输出去,实现直播目的。

对应设计架构图的“发布端”,编码后的音视频数据,按照协议打包后,推送到流媒体服务器(如RTMP服务器,自建服务,可以考虑SRS或者nginx服务器,如果是RTSP服务器,可以考虑苹果官方的darwin streaming server)。

这种方案的设计,一般是一对多设计模型,接收端接收RTMP或RTSP流,然后解析音视频数据,解码、同步音视频数据,并绘制,实现整体的直播解决方案。

以下是设计架构图:

模块设计

  • 自有框架,易于扩展,自适应算法让延迟更低、采集编码传输效率更高;
  • 所有功能以接口形式提供,所有状态,均有event回调,支持断网自动重连;
  • 模块化设计,可和大牛直播RTSP或RTMP直播播放模块组合实现流媒体数据转发、连麦、一对一互动等场景;
  • 推送叠加以层级模式提供,开发者可以自行组合数据源(如多摄像头/屏幕/水印叠加);
  • 支持外部YUV/RGB/H.264/AAC/SPEEX/PCMA/PCMU数据源接入;
  • 所有参数均可通过SDK接口单独设置,亦可通过默认参数,傻瓜式设置;
  • 推送、录像、内置轻量级RTSP服务模块完全分离,可单独使用亦可组合使用。

功能设计

  • [本地预览]支持摄像头/屏幕/合成数据实时预览功能;
  • [摄像头反转/旋转]支持摄像头水平反转、垂直反转、0°/90°/180°/270°旋转;
  • [摄像头采集]除常规YUV格式外,还支持MJPEG格式的摄像头采集;
  • [RTMP推流]超低延时的RTMP协议直播推流SDK(Windows 64位库支持RTMP扩展H.265推送);
  • [视频格式]Windows支持H.264/H.265编码;
  • [音频格式]支持AAC编码和Speex编码;
  • [音频编码]支持Speex推送、Speex编码质量设置;
  • [软硬编码参数配置]支持gop间隔、帧率、bit-rate设置;
  • [软编码参数配置]支持软编码profile、软编码速度、可变码率设置;
  • [多实例推送]支持多实例推送(如同时推送屏幕/摄像头和外部数据);
  • [RTMP扩展H.265]Windows/Android推送SDK支持RTMP扩展H.265推送,Windows针对摄像头采集软编码,使用H.265可变码率,带宽大幅节省,效果直逼传统H.265编码摄像头;
  • [多分辨率支持]支持摄像头或屏幕多种分辨率设置;
  • [Windows推屏]支持屏幕裁剪、窗口采集、屏幕/摄像头数据合成等多种模式推送;
  • [事件回调]支持各种状态实时回调;
  • [水印]Windows平台支持文字水印、png水印、实时遮挡;
  • [复杂网络处理]支持断网重连等各种网络环境自动适配;
  • [动态码率]支持根据网络情况自动调整推流码率;
  • [实时静音]支持推送过程中,实时静音/取消静音;
  • [实时快照]支持推流过程中,实时快照;
  • [纯音频推流]支持仅采集音频流并发起推流功能;
  • [纯视频推流]支持特殊场景下的纯视频推流功能;
  • [降噪]支持环境音、手机干扰等引起的噪音降噪处理、自动增益、VAD检测;
  • [外部编码前视频数据对接]支持YUV数据对接;
  • [外部编码前音频数据对接]支持PCM对接;
  • [外部编码后视频数据对接]支持外部H.264数据对接;
  • [外部编码后音频数据对接]外部AAC/PCMA/PCMU/SPEEX数据对接;
  • [扩展录像功能]完美支持和录像SDK组合使用;
  • [服务器兼容]支持支持自建服务器(如Nginx、SRS)或CDN。

集成和使用说明

demo说明

  • Windows平台RTMP/RTSP直播推送模块对外提供C++/C#两套接口,对外提供32/64位库,C++和C#接口一一对应,C#接口比C++接口增加前缀NT_PB_。
  • WIN-PublisherSDK-CPP-Demo:推送端SDK对应的C++接口的demo;
  • WIN-PublisherSDK-CSharp-Demo:推送端SDK对应的C#接口的demo;
  • 推送端模块支持Win7及以上系统。
  • 本demo基于VS2013开发。

C++头文件:

  • [类型定义]nt_type_define.h
  • [Log定义]smart_log.h
  • [Log定义]smart_log_define.h
  • [音视频类型定义]nt_common_media_define.h
  • [base code定义]nt_base_code_define.h
  • [publisher接口]nt_smart_publisher_define.h
  • [publisher接口]nt_smart_publisher_sdk.h

C#头文件:

  • [Log定义]smart_log.cs
  • [Log定义]smart_log_define.cs
  • [base code定义]nt_base_code_define.cs
  • [publisher接口]nt_smart_publisher_define.cs
  • [publisher参数定义]nt_smart_publisher_sdk.cs

相关Lib:

  • SmartLog.dll
  • SmartLog.lib
  • SmartPublisherSDK.dll
  • SmartPublisherSDK.lib
  • avcodec-56.dll
  • avdevice-56.dll
  • avfilter-5.dll
  • avformat-56.dll
  • avutil-54.dll
  • postproc-53.dll
  • swresample-1.dll
  • swscale-3.dll

集成步骤

  1. 把lib目录下debug/release库拷贝到需要集成的工程对应的debug或release目录下(确保32位/64位库debug/release目录一一对应);

lib目录如下:

    1. 32位debug库:debug
    2. 32位release库:release
    3. 64位debug库:x64\debug
    4. 64位release库:x64\release

2. 相关cs头文件,加入需要集成的工程;

3. 在需要集成的工程,右键->Properties->Application->Assembly name,写入“SmartPulisherDemo”。

功能详解

考虑到Windows平台推送端SDK功能相对复杂,以问答式:

1视频采集设置

1. 屏幕和摄像头相互切换:用于在线教育或者无纸化等场景,推送或录像过程中,随时切换屏幕或摄像头数据(切换数据源),如需实时切换,点击页面“切换到摄像头”按钮即可;

2. 设置遮盖层,用于设定一个长方形或正方形区域(可自指定区域大小),遮盖不想给用户展示的部分;

3. 水印:添加PNG水印,支持推送或录像过程中,随时添加、取消水印;

4. 摄像头叠加到屏幕:意在用于同屏过程中,主讲人摄像头悬浮于屏幕之上(可指定叠加坐标),实现双画面展示,推送或录像过程中,可以随时取消摄像头叠加;

5. 屏幕叠加到摄像头:同4,效果展示,实际根据需求实现;

6. 采集桌面:可以通过点击“选择屏幕区域”获取采集区域,并可在采集过程中,随时切换区域位置,如不设定,默认全屏采集;

7. 使用DXGI采集屏幕,采集时停用Aero;

8. 采集窗口:可设定需要采集的窗口,窗口放大或缩小,推送端会自适应码率和分辨率;

9. 采集帧率(帧/秒):默认屏幕采集8帧,可根据实际场景需求设定到期望帧率;

10. 缩放屏幕大小缩放比:用于高清或超高清屏,通过设定一定的比例因子,缩放屏幕采集分辨率;

11. 采集摄像头:可选择需要采集的摄像头、采集分辨率、帧率、是否需要水平或者垂直反转、是否需要旋转;

追加提问:

问题[确认数据源]:采集桌面还是摄像头?如果桌面,全屏还是部分区域?

回答:

如果是摄像头:可以选择摄像头列表,然后分辨率、帧率。

如果是屏幕:默认帧率是5帧,可以根据实际场景调整,选取屏幕区域,可以实时拉取选择需要采集或录像区域;

如果是叠加模式:可选择摄像头叠加到屏幕,还是屏幕叠加到摄像头;

更高需求的用户,可以设置水印或应用层遮盖。

问题:如果是摄像头,采集到的摄像头角度不对怎么办?

回答:我们支持摄像头镜像和翻转设置,摄像头可通过SDK接口轻松实现水平/垂直翻转、镜像效果。

2 视频码率控制

我选可变码率还是平均码率?

回答:可变码率的优势在于,如果屏幕或摄像头变化不大,码率超低,特别是H.265编码,平均码率,码率比较均匀,需设置平均码率+最大码率,一般摄像头采集建议选择可变码率,屏幕采集选择平均码率,如需采用可变码率,请取消“使用平均码率”选项。

265编码还是H.264编码?

回答:Windows 64位库支持H.265编码,如果推RTMP流,需要服务器支持RTMP H.265扩展,播放器SDK,也需要同步支持RTMP H.265扩展播放。

如果是轻量级RTSP服务SDK对接的话,只需要播放器支持RTSP H.265即可。

如果推摄像头数据,建议采用可变码率+H.265编码。

如何设置码率参数更合理?

回答:

关键帧间隔:一般来说,设置到帧率的2-4倍,比如帧率20,关键帧间隔可以设置到40-80;

平均码率:可以点击“获取视频码率默认值”,最大码率是平均码率的2倍;

视频质量:如果使用可变码率,建议采用大牛直播SDK默认推荐视频质量值;

编码速度:如高分辨率,建议1-3,值越小,编码速度越快;

H.264 Profile:默认baseline profile,可根据需要,酌情设置High profile;

NOTE:点击“推送”或“录像”或启动内置RTSP服务SDK之前,请务必设置视频码率,如不想手动设置,请点击“获取视频码率默认值”!!!

3 音频采集设置

问答式:采集音频吗?如果采集,采集麦克风还是扬声器的,亦或混音?

回答:

如果想采集电脑输出的音频(比如音乐之类),可以选择“采集扬声器”;

如果想采集麦克风音频,可以选择“采集麦克风”,并选择相关设备;

如果两个都想采集,可以两个都选择,混音输出。

4 音频编码

问题:是AAC还是SPEEX?

回答:我们默认是AAC编码模式,如果需要码率更低,可以选择SPEEX编码模式,当然我们的AAC编码码率也不高。

5 音频处理

问题:我想过滤背景噪音怎么办?

回答:选中“噪音抑制”,“噪音抑制“请和“自动增益控制”组合使用,“端点检测(VAD)”可选设置。

问题:我想做一对一互动怎么办?

回答:选中“回音消除”,可以和“噪音抑制”、“自动增益控制”组合使用。

问题:我推送或者录像过程中,随时静音怎么办?

回答:推送过程中,随时选择或取消选择“静音”功能。

6多路推送

问题:我想同时推送到多个url怎么办(比如一个内网服务器,一个外网服务器)?

回答:同时填写多个url,然后点推送即可。

7 截图(快照)

问题:我想推送或者录像过程中,截取当前图像怎么办?

回答:那就设置好截图路径,推送或录像过程中,随时点击“截图”。

8 录像

问题:我还想录像,怎么办?

回答:设置录像文件存放目录,文件前缀、单个文件大小,是否加日期、时间,随时录制即可,此外,我们的SDK还支持录像过程中,暂停录像,恢复录像。

9 实时预览

问题:我还想看看视频特别是合成后的效果,怎么办?

回答:点击页面的“预览”按钮,就可以看到。

接口调用时序(以C#为例)

如需下载demo源码工程,可以到 Github 下载 “Windows平台RTMP|RTSP推送SDK、内置RTSP服务SDK、录像SDK”,C++或者C#的都有。

1 初始化

NT_PB_Init

如需配置log路径,请在NT_PB_Init之前,做如下设置(目录可自行指定):

// 设置日志路径(请确保目录存在)

//String log_path = “D:\\pulisherlog”;

//NTSmartLog.NT_SL_SetPath(log_path);

2 Open

NT_PB_Open

3 设置回调事件

  • NT_PB_SetEventCallBack:设置事件回调,如果想监听事件的话,建议调用Open成功后,就调用这个接口
  • NT_PB_SetVideoPacketTimestampCallBack:设置视频包时间戳回调
  • NT_PB_SetPublisherStatusCallBack:设置推送状态回调

4 设置屏幕裁剪

  • NT_PB_SetScreenClip:设置屏幕裁剪
  • NT_PB_MoveScreenClipRegion:移动屏幕剪切区域,这个接口只能推送或者录像中调用

5 屏幕选取工具

  • NT_PB_OpenScreenRegionChooseTool:打开一个屏幕选取工具的toolHandle
  • NT_PB_MoveScreenClipRegion:移动屏幕剪切区域,这个接口只能推送或者录像中调用
  • NT_PB_AllocateImage:分配Image, 分配后,SDK内部会初始化这个结构体, 失败的话返回NULL
  • NT_PB_FreeImage:释放Image, 注意一定要调用这个接口释放内存,如果在你自己的模块中释放,Windows会出问题的
  • NT_PB_CloneImage:克隆一个Image, 失败返回NULL
  • NT_PB_CopyImage:拷贝Image, 会先释放dst的资源,然后再拷贝
  • NT_PB_SetImagePlane: 给图像一个面设置数据,如果这个面已经有数据,将会释放掉再设置
  • NT_PB_LoadImage:加载PNG图片

6 设置屏幕采集参数

  • NT_PB_EnableDXGIScreenCapturer:允许使用DXGI屏幕采集方式, 这种方式需要win8及以上系统才支持
  • NT_PB_DisableAeroScreenCapturer:采集屏幕时停用Aero, 这个只对win7有影响,win8及以上系统, 微软已经抛弃了Aero Glass效果
  • NT_PB_CheckCapturerWindow:判断顶层窗口能否能被捕获, 如果不能被捕获的话返回NT_ERC_FAILED(采集窗口)
  • NT_PB_SetCaptureWindow:设置要捕获的窗口的句柄(采集窗口)

7 设置摄像头采集参数

  • NT_PB_StartGetVideoCaptureDeviceImage:获取句柄,且保存句柄
  • NT_PB_FlipVerticalVideoCaptureDeviceImage:上下反转设备图像
  • NT_PB_FlipHorizontalVideoCaptureDeviceImage:水平反转设备图像
  • NT_PB_RotateVideoCaptureDeviceImage:旋转设备图像, 顺时针旋转
  • NT_PB_GetVideoCaptureDeviceNumber:获取摄像头数量
  • NT_PB_GetVideoCaptureDeviceInfo:返回摄像头设备信息
  • NT_PB_GetVideoCaptureDeviceCapabilityNumber:返回摄像头能力数
  • NT_PB_GetVideoCaptureDeviceCapability:返回摄像头能力
  • NT_PB_DisableVideoCaptureResolutionSetting:

在多个实例推送多路时,对于一个摄像头来说,所有实例只能共享摄像头,那么只有一个实例可以改变摄像头分辨率,其他实例使用这个缩放后的图像;

在使用多实例时,调用这个接口禁止掉实例的分辨率设置能力.只留一个实例能改变分辨,如果不设置,行为未定义;

这个接口必须在 SetLayersConfig, AddLayerConfig 之前调用。

  • NT_PB_StartVideoCaptureDevicePreview: 启动摄像头预览
  • NT_PB_FlipVerticalCameraPreview:上下反转摄像头预览图像
  • NT_PB_FlipHorizontalCameraPreview:水平反转摄像头预览图像
  • NT_PB_RotateCameraPreview:旋转摄像头预览图像, 顺时针旋转
  • NT_PB_VideoCaptureDevicePreviewWindowSizeChanged:告诉SDK预览窗口大小改变
  • NT_PB_StopVideoCaptureDevicePreview:停止摄像头预览
  • NT_PB_GetVideoCaptureDeviceImage:调用这个接口可以获取摄像头图像
  • NT_PB_StopGetVideoCaptureDeviceImage:停止获取摄像头图像
  • NT_PB_SetVideoCaptureDeviceBaseParameter:设置摄像头信息
  • NT_PB_FlipVerticalCamera上下反转摄像头图像
  • NT_PB_FlipHorizontalCamera:水平反转摄像头图像
  1. NT_PB_RotateCamera:旋转摄像头图像, 顺时针旋转

8 视频合成图层类型

public enum NT_PB_E_LAYER_TYPE : int

{

NT_PB_E_LAYER_TYPE_SCREEN = 1,                  // 屏幕层

NT_PB_E_LAYER_TYPE_CAMERA = 2,                  // 摄像头层

NT_PB_E_LAYER_TYPE_RGBA_RECTANGLE = 3,          // RGBA矩形

NT_PB_E_LAYER_TYPE_IMAGE = 4,                   // 图片层

NT_PB_E_LAYER_TYPE_EXTERNAL_VIDEO_FRAME = 5,    // 外部视频数据层

NT_PB_E_LAYER_TYPE_WINDOW = 6, // 窗口层

}

9 音视频源类型

/*定义Video源选项*/

public enum NT_PB_E_VIDEO_OPTION : uint

{

NT_PB_E_VIDEO_OPTION_NO_VIDEO = 0x0,

NT_PB_E_VIDEO_OPTION_SCREEN = 0x1, // 采集屏幕

NT_PB_E_VIDEO_OPTION_CAMERA = 0x2, // 摄像头采集

NT_PB_E_VIDEO_OPTION_LAYER = 0x3,  // 视频合并,比如桌面叠加摄像头等

NT_PB_E_VIDEO_OPTION_ENCODED_DATA = 0x4, // 已经编码的视频数据,目前支持H264

NT_PB_E_VIDEO_OPTION_WINDOW = 0x5, // 采集窗口

}

/*定义Auido源选项*/

public enum NT_PB_E_AUDIO_OPTION : uint

{

NT_PB_E_AUDIO_OPTION_NO_AUDIO = 0x0,

NT_PB_E_AUDIO_OPTION_CAPTURE_MIC = 0x1,           // 采集麦克风音频

NT_PB_E_AUDIO_OPTION_CAPTURE_SPEAKER = 0x2,           // 采集扬声器

NT_PB_E_AUDIO_OPTION_CAPTURE_MIC_SPEAKER_MIXER = 0x3,    // 麦克风扬声器混音

NT_PB_E_AUDIO_OPTION_ENCODED_DATA = 0x4, // 编码后的音频数据,目前支持AAC, speex宽带(wideband mode)

}

10 视频编码接口

  • NT_PB_SetVideoEncoderType:设置编码类型, 当前支持h264和h265(注意:h265只有64位sdk库支持, 在32位库上设置会失败);
  • NT_PB_SetVideoQuality:设置视频质量, 范围[0-20], 默认是10, 值越小质量越好,但码率会越大
  • NT_PB_SetVideoQualityV2:设置视频质量, 范围[1-50], 值越小视频质量越好,但码率会越大. 请优先考虑默认值;
  • NT_PB_SetFrameRate:设置帧率
  • NT_PB_SetVideoMaxBitRate:设置最大视频码率, 单位kbps
  • NT_PB_AddVideoEncoderBitrateGroupItem:

* 在一些特殊场景下, 视频分辨率会改变, 如果设置一个固定码率的的话,当视频分辨率变大的时候会变的模糊,变小的话又会浪费码率

* 所以提供可以设置一组码率的接口,满足不同分辨率切换的需求

* 规则: 比如设置两组分辨率 640*360, 640*480, 那么当分辨率小于等于640*360时都使用640*360的码率,

* 当分辨率大于640*360且小于等于640*480时,就使用640*480的码率,如果分辨率大于640*480 那就使用640*480的分辨率

* 为了设置的更准确, 建议多划分几组, 让区间变小

* 调用这个接口每次设置一组,设置多组就调用多次

* item对应 NT_PB_VideoEncoderBitrateGroupItem

  • NT_PB_ClearVideoEncoderBitrateGroup:清除视频码率组
  • NT_PB_SetVideoKeyFrameInterval:设置关键帧间隔, 比如1表示所有帧都是关键帧,10表示每10帧里面一个关键帧,25表示每25帧一个关键帧
  • NT_PB_SetVideoEncoderProfile:设置H264 profile,1: H264 baseline(默认值). 2: H264 main. 3. H264 high
  • NT_PB_SetVideoEncoderSpeed:设置H264编码速度,speed: 范围是 1 到 6,  值越小,速度越快,质量也越差
  • NT_PB_SetVideoCompareSameImage:设置是否对图像进行相同比较,相同图像比较一般在采集桌面时有一定好处,可能能降低码率
  • NT_PB_SetVideoMaxKeyFrameInterval:设置视频最大关键帧间隔, 这个接口一般不使用,这里是用来配合SetVideoCompareSameImage接口的,比如开启图像比较后,SDK发现连续20s图像都是相同的,但播放端需要收到关键帧才能解码播放,所以需要一个限制

11 音频编码接口

  • NT_PB_GetAuidoInputDeviceNumber:获取系统音频输入设备数
  • NT_PB_GetAuidoInputDeviceName:获取音频输入设备名称
  • NT_PB_SetPublisherAudioCodecType:设置推送音频编码类型,type: 1:使用AAC编码, 2:使用speex编码, 其他值返回错误
  • NT_PB_SetPublisherSpeexEncoderQuality:设置推送Speex编码质量
  • NT_PB_SetAuidoInputDeviceId:设置音频输入设备ID
  • NT_PB_IsCanCaptureSpeaker:检查是否能捕获扬声器音频

12 音频处理接口

  • NT_PB_SetEchoCancellation:设置回音消除
  • NT_PB_SetNoiseSuppression:设置音频噪音抑制
  • NT_PB_SetAGC:设置音频自动增益控制
  • NT_PB_SetVAD:设置端点检测(Voice Activity Detection (VAD))

13 图层合成等接口

  • NT_PB_SetLayersConfig:设置视频合成层, 传入的是一个数组, 请正确填充每一层
  • NT_PB_ClearLayersConfig:清除所有层配置,注意这个接口只能在推送或者录像之前调用,否则结果未定义
  • NT_PB_AddLayerConfig: 增加层配置,注意这个接口只能在推送或者录像之前调用,否则结果未定义
  • NT_PB_EnableLayer:动态禁止或者启用层
  • NT_PB_UpdateLayerConfigV2:更新层相关配置, 注意不是层的所有字段都可以更新,只是部分可以更新,并且有些层没有字段可以更新,传入的参数,SDK只选择能更新的字段更新,不能更新的字段会被忽略
  • NT_PB_UpdateLayerRegion:修改图层
  • NT_PB_PostLayerImage:给index层投递Image数据,目前主要是用来把rgb和yuv视频数据传给相关层
  • NT_PB_SetParam:万能接口, 设置参数, 大多数问题, 这些接口都能解决
  • NT_PB_GetParam:万能接口, 得到参数, 大多数问题,这些接口都能解决

15 RTMP推送-设置推送RTMP Url

NT_PB_SetURL:rtmp推送url设置

16 RTMP推送-启动推送RTMP流

NT_PB_StartPublisher

17 RTMP推送-停止推送RTMP流

NT_PB_StopPublisher:注意,此接口和NT_PB_StartPublisher配套使用

18 RTSP推送-设置传输方式(TCP/UDP)

NT_PB_SetPushRtspTransportProtocol:设置推送rtsp传输方式,一般服务器可同时支持RTSP TCP或UDP传输模式,部分服务器只支持TCP或UDP模式。其中,transport_protocol: 1表示UDP传输rtp包; 2表示TCP传输rtp包. 默认是1, UDP传输。

19 RTSP推送-设置推送RTSP Url

NT_PB_SetPushRtspURL:注意,RTSP推送时,确保服务器推送URL可用。

20 RTSP推送-启动推送RTSP流

NT_PB_StartPushRtsp

21 RTSP推送-启动推送RTSP流

NT_PB_StopPushRtsp:注意,此接口和NT_PB_StartPushRtsp配套使用。

22 RTMP/RTSP推送端录像

  • NT_PB_SetRecorderDirectory:设置本地录像目录, 必须是英文目录,否则会失败
  • NT_PB_SetRecorderFileMaxSize:设置单个录像文件最大大小, 当超过这个值的时候,将切割成第二个文件
  • NT_PB_SetRecorderFileNameRuler:设置录像文件名生成规则
  • NT_PB_StartRecorder:启动录像
  • NT_PB_PauseRecorder:暂停录像,is_pause: 1表示暂停, 0表示恢复录像, 输入其他值将调用失败
  • NT_PB_StopRecorder:停止录像

23 实时静音(实时调用)

NT_PB_SetMute:设置推送实时静音

24 快照(实时调用)

NT_PB_CaptureImage:推送或者录像过程中,实时快照

25 Close

NT_PB_Close:调用这个接口之后handle失效

26 Uninit

NT_PB_UnInit:这个是最后一个调用的接口

以上是我们的设计模块部分资料,感兴趣的开发者,可以酌情参考。

大牛直播SDK:如何设计一款跨平台低延迟的RTMP/RTSP直播播放器

开发背景

2015年,当我们试图在市面上找一款专供直播播放使用的低延迟播放器,来配合测试我们的RTMP推送模块使用时,居然发现没有一款好用的,市面上的,如VLC或Vitamio,说白了都是基于FFMPEG,在点播这块支持格式很多,也非常优异,但是直播这块,特别是RTMP,延迟要几秒钟,对如纯音频、纯视频播放,快速启播、网络异常状态处理、集成复杂度等各方面,支持非常差,而且因为功能强大,bug很多,除了行业内资深的开发者能驾驭,好多开发者甚至连编译整体环境,都要耗费很大的精力。

我们的直播播放器,始于Windows平台,Android和iOS同步开发,基于上述开源播放器的各种缺点,我们考虑全自研框架,确保整体设计跨平台,再保障播放流程度的前提下,尽可能的做到毫秒级延迟,接口设计三个平台统一化,确保多平台集成复杂度降到最低。

整体方案架构

RTMP或RTSP直播播放器,目标很明确,从RTMP服务器(自建服务器或CDN)或RTSP服务器(或NVR/IPC/编码器等)拉取流数据,完成数据解析、解码、音视频数据同步、绘制。

具体对应下图“接收端”部分:

初期模块设计目标

  • 自有框架,易于扩展,自适应算法让延迟更低、解码绘制效率更高;
  • 支持各种异常网络状态处理,如断网重连、网络抖动等控制;
  • 有Event状态回调,确保开发者可以了解到播放端整体的状态,从纯黑盒不可控,到更智能的了解到整体播放状态;
  • 支持多实例播放;
  • 视频支持H.264,音频支持AAC/PCMA/PCMU;
  • 支持缓冲时间设置(buffer time);
  • 实时静音。

经过迭代后的功能

  • [支持播放协议]RTSP、RTMP,毫秒级延迟;
  •  [多实例播放]支持多实例播放;
  •  [事件回调]支持网络状态、buffer状态等回调;
  •  [视频格式]支持RTMP扩展H.265,H.264;
  •  [音频格式]支持AAC/PCMA/PCMU/Speex;
  •  [H.264/H.265软解码]支持H.264/H.265软解;
  •  [H.264硬解码]Windows/Android/iOS支持H.264硬解;
  •  [H.265硬解]Windows/Android/iOS支持H.265硬解;
  •  [H.264/H.265硬解码]Android支持设置Surface模式硬解和普通模式硬解码;
  •  [缓冲时间设置]支持buffer time设置;
  •  [首屏秒开]支持首屏秒开模式;
  •  [低延迟模式]支持类似于线上娃娃机等直播方案的超低延迟模式设置(公网200~400ms);
  •  [复杂网络处理]支持断网重连等各种网络环境自动适配;
  •  [快速切换URL]支持播放过程中,快速切换其他URL,内容切换更快;
  •  [音视频多种render机制]Android平台,视频:surfaceview/OpenGL ES,音频:AudioTrack/OpenSL ES;
  •  [实时静音]支持播放过程中,实时静音/取消静音;
  •  [实时快照]支持播放过程中截取当前播放画面;
  •  [只播关键帧]Windows平台支持实时设置是否只播放关键帧;
  •  [渲染角度]支持0°,90°,180°和270°四个视频画面渲染角度设置;
  •  [渲染镜像]支持水平反转、垂直反转模式设置;
  •  [实时下载速度更新]支持当前下载速度实时回调(支持设置回调时间间隔);
  •  [ARGB叠加]Windows平台支持ARGB图像叠加到显示视频(参看C++的DEMO);
  •  [解码前视频数据回调]支持H.264/H.265数据回调;
  •  [解码后视频数据回调]支持解码后YUV/RGB数据回调;
  •  [解码后视频数据缩放回调]Windows平台支持指定回调图像大小的接口(可以对原视图像缩放后再回调到上层);
  •  [解码前音频数据回调]支持AAC/PCMA/PCMU/SPEEX数据回调;
  •  [音视频自适应]支持播放过程中,音视频信息改变后自适应;
  •  [扩展录像功能]支持RTSP/RTMP H.264、扩展H.265流录制,支持PCMA/PCMU/Speex转AAC后录制,支持设置只录制音频或视频等;

RTMP、RTSP直播播放开发设计考虑的点

1. 低延迟:大多数RTSP的播放都面向直播场景,所以,如果延迟过大,严重影响体验,所以,低延迟是衡量一个好的RTSP播放器非常重要的指标,目前大牛直播SDK的RTSP直播播放延迟比开源播放器更优异,而且长时间运行下,不会造成延迟累积;

2. 音视频同步处理有些播放器为了追求低延迟,甚至不做音视频同步,拿到audio video直接播放,导致a/v不同步,还有就是时间戳乱跳等各种问题,大牛直播SDK提供的播放器,具备好的时间戳同步和异常时间戳矫正机制;

3. 支持多实例:大牛直播SDK提供的播放器支持同时播放多路音视频数据,比如4-8-9窗口,大多开源播放器对多实例支持不太友好;

4. 支持buffer time设置:在一些有网络抖动的场景,播放器需要支持buffer time设置,一般来说,以毫秒计,开源播放器对此支持不够友好;

5. TCP/UDP模式设定自动切换:考虑到好多服务器仅支持TCP或UDP模式,一个好的RTSP播放器需要支持TCP/UDP模式设置,如链接不支持TCP或UDP,大牛直播SDK可自动切换,,开源播放器不具备自动切换TCP/UDP能力;

6. 实时静音:比如,多窗口播放RTSP流,如果每个audio都播放出来,体验非常不好,所以实时静音功能非常必要,开源播放器不具备实时静音功能;

7. 视频view旋转:好多摄像头由于安装限制,导致图像倒置,所以一个好的RTSP播放器应该支持如视频view实时旋转(0° 90° 180° 270°)、水平反转、垂直反转,开源播放器不具备此功能;

8. 支持解码后audio/video数据输出:大牛直播SDK接触到好多开发者,希望能在播放的同时,获取到YUV或RGB数据,进行人脸匹配等算法分析,开源播放器不具备此功能;

9. 实时快照:感兴趣或重要的画面,实时截取下来非常必要,一般播放器不具备快照能力,开源播放器不具备此功能;

10. 网络抖动处理(如断网重连):稳定的网络处理机制、支持如断网重连等,开源播放器对网络异常处理支持较差;

11. 长期运行稳定性:不同于市面上的开源播放器,大牛直播SDK提供的Windows平台RTSP直播播放SDK适用于数天长时间运行,开源播放器对长时间运行稳定性支持较差;

12. log信息记录:整体流程机制记录到LOG文件,确保出问题时,有据可依,开源播放器几无log记录。

13. 实时下载速度反馈:大牛直播SDK提供音视频流实时下载回调,并可设置回调时间间隔,确保实时下载速度反馈,以此来监听网络状态,开源播放器不具备此能力;

14. 异常状态处理Event状态回调如播放的过程中,断网、网络抖动、等各种场景,大牛直播SDK提供的播放器可实时回调相关状态,确保上层模块感知处理,开源播放器对此支持不好;

15. 关键帧/全帧播放实时切换:特别是播放多路画面的时候,如果路数过多,全部解码、绘制,系统资源占用会加大,如果能灵活的处理,可以随时只播放关键帧,全帧播放切换,对系统性能要求大幅降低。

接口设计

好多开发者,在初期设计接口的时候,如果没有足够的音视频背景,很容易反复推翻之前的设计,我们以Windows平台为例,共享我们的设计思路,如需要下载demo工程源码,可以到 GitHub 下载参考:

总结

总的来说,无论是基于开源播放器二次开发,还是全自研,一个好的RTMP播放器或RTSP播放器,设计的时候,更多考虑的应该是如何做的更灵活、稳定,单纯的几个接口,很难满足通用化的产品诉求。

以下共勉:厚积薄发,登上山顶,不是为了饱览风光,是为了寻找更高的山峰!

轻量级RTSP服务模块和RTSP推流模块适用场景区别

好多开发者一直搞不清我们轻量级RTSP服务SDK和RTSP推流SDK的区别,以下是相关区别:

1. 轻量级RTSP服务模块:轻量级RTSP服务解决的核心痛点是避免用户或者开发者单独部署RTSP或者RTMP服务,实现本地的音视频数据(如摄像头、麦克风),编码后,汇聚到内置RTSP服务,对外提供可供拉流的RTSP URL,轻量级RTSP服务,适用于内网环境下,对并发要求不高的场景,支持H.264/H.265,支持RTSP鉴权、单播、组播模式,考虑到单个服务承载能力,我们支持同时创建多个RTSP服务,并支持获取当前RTSP服务会话连接数。

以下是接口详解(以Android平台为例):

Android内置轻量级RTSP服务SDK接口详解

调用描述

接口

接口描述

常规功能

Android RTMP推送端所有功能依然有效,亦可同时推送RTMP

SmartRTSPServerSDK

初始化RTSP Server

InitRtspServer

Init rtsp server(和UnInitRtspServer配对使用,即便是启动多个RTSP服务,也只需调用一次InitRtspServer,请确保在OpenRtspServer之前调用)

创建一个rtsp server

OpenRtspServer

创建一个rtsp server,返回rtsp server句柄

设置端口

SetRtspServerPort

设置rtsp server 监听端口, 在StartRtspServer之前必须要设置端口

设置鉴权用户名、密码

SetRtspServerUserNamePassword

设置rtsp server 鉴权用户名和密码, 这个可以不设置,只有需要鉴权的再设置

获取rtsp server当前会话数

GetRtspServerClientSessionNumbers

获取rtsp server当前的客户会话数, 这个接口必须在StartRtspServer之后再调用

启动rtsp server

StartRtspServer

启动rtsp server

停止rtsp server

StopRtspServer

停止rtsp server

关闭rtsp server

CloseRtspServer

关闭rtsp server

UnInit rtsp server

UnInitRtspServer

UnInit rtsp server(和InitRtspServer配对使用,即便是启动多个RTSP服务,也只需调用一次UnInitRtspServer)

SmartRTSPServerSDK供Publisher调用的接口

设置rtsp的流名称

SetRtspStreamName

设置rtsp的流名称

给要发布的rtsp流设置rtsp server

AddRtspStreamServer

给要发布的rtsp流设置rtsp server, 一个流可以发布到多个rtsp server上,rtsp server的创建启动请参考OpenRtspServer和StartRtspServer接口

清除设置的rtsp server

ClearRtspStreamServer

清除设置的rtsp server

启动rtsp流

StartRtspStream

启动rtsp流

停止rtsp流

StopRtspStream

停止rtsp流

2. RTSP推流模块:RTSP推流模块,和RTMP推流模块类似,适用于内网或公网环境下,主要适用于第三方RTSP服务对接,如darwin stream server,或者第三方RTSP服务平台,如视频分析平台等特定场景的服务器,支持H.264/H.265,支持TCP、UDP传输模式设定,也支持鉴权服务,RTSP协议的优势主要在于UDP这块,但是UDP数据包,公网容易被block住,而且,网络不稳定容易丢包,所以,能用RTMP推流的场景,一般建议走RTMP,需要特定系统对接的,再走RTSP。

以下是接口详解(以Android平台为例):

调用描述

接口

接口描述

设置推送RTSP传输方式

SetPushRtspTransportProtocol

transport_protocol:1表示UDP传输rtp包; 2表示TCP传输rtp包

设置推送RTSP的URL

SetPushRtspURL

设置推送RTSP的URL

开始RTSP推送

StartPushRtsp

启动推送RTSP流

停止RTSP推送

StopPushRtsp

停止推送RTSP流

参考资料:https://github.com/daniulive/SmarterStreaming

基于智慧教室|无纸化会议的新选择:RTMP解决方案

基于智慧教室或是会议的技术方案,一般主要是涉及到屏幕采集和推送,整体技术方案这块,一般建议走RTMP,说到这里,好人开发者提到,市面上也有RTSP的技术方案,甚至RTSP组播方案,这块,大牛直播SDK Github 也做过相关对比,总的来说60人智慧教室或类似同屏场景下,最可靠的还是RTMP的解决方案(不赘述,具体可自行测试对比)。

有人说,RTMP延迟大,这种说法,相对片面,好多是由于推拉流模块本身问题导致(如果服务器系NIGNX或SRS,基本可排除服务器转发导致的大时延,不要再赖服务器了),从我们官方和实际场景来看,RTMP整体技术方案,延迟可做到1秒内,毫秒级。

整体设计方案如下

注意事项

1. 组网:无线组网,需要好的AP模块才能撑得住大的并发流量,推送端到AP,最好是有线网链接;

2. 服务器部署:如果Windows平台,可以考虑NGINX,如果是Linux,可以考虑SRS或NGINX,服务器可以和Windows平台的教师机部署在一台机器;

3. 教师端:如教师有移动的PAD,可以直接推到RTMP服务器,然后共享出去;

4. 学生端:直接拉取RTMP流播放即可;

5. 教师和学生互动:学生端如需作为示范案例,屏幕数据共享给其他同学,只需请求同屏,数据反推到RTMP服务器,其他学生查看即可。

6. 扩展监控:如果需要更进一步的技术方案,如教师端想监控学生端的屏幕情况,可以有两种方案,如学生端直接推RTMP过来,或者,学生端启动内置RTSP服务,教师端想看的时候,随时看即可(亦可轮询播放)。

以下分平台介绍相关配置选项

Windows平台RTMP推送端

对应DEMO:SmartPublisherDemo.exe

1. 如果采集屏幕,只要采集部分区域的话,可以点击“选取屏幕区域”按钮,选择需要采集的区域,采集推送过程中,可以移动采集区域;

2. 如果是高分屏(如有些采集设备,是4K屏,原始分辨率过高),用户又不想推这么高的分辨率的话,可以选中“缩放屏幕大小”,并指定缩放比例,可以先缩放,后编码推送数据;

3. 设置采集帧率:如果是PPT/Word文档类,一般8-12帧足矣,如果是电影之类,可以设置到20-30帧不等,关键帧间隔一般设置到帧率的2-4倍,屏幕推送的话,建议平均码率模式;

4. 如果需要采集电脑端输出的声音,可以选中“采集扬声器”,如果需要采集外部麦克风的音频,选择“采集麦克风”即可,并选择对应的采集设备;

5. 设置下推送的RTMP URL,然后,点击“推送”,就可以了;

6. 如果想预览推送出去的数据,点击“预览”即可,想停止预览的话,点击“停止预览”即可。

Android平台RTMP屏幕推送端

对应工程:SmartServicePublisherV2

需要注意的事项:

1. Android 8.0及以上版本设备,需要加入省电优化白名单,6.0以上版本,需要动态获取audio权限,具体代码如下:

        //加入省电优化白名单,以免8.0及以上版本设备后台运行超过一分钟被自动停掉
        //if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
        if (Build.VERSION.SDK_INT >=26)
        {
            if(!isIgnoringBatteryOptimizations())
            {
                gotoSettingIgnoringBatteryOptimizations();
            }
        }

        //6.0及以上版本,动态获取Audio权限
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
        {
            RequestAudioPermission();
        }


    //拉起请求加入省电白名单弹窗
    private void gotoSettingIgnoringBatteryOptimizations() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            try {
                Intent intent = new Intent();
                String packageName = getPackageName();
                intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
                intent.setData(Uri.parse("package:" + packageName));
                startActivityForResult(intent, REQUEST_IGNORE_BATTERY_CODE);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    //动态获取Audio权限
    private void RequestAudioPermission()
    {
        if (PackageManager.PERMISSION_GRANTED ==  ContextCompat.checkSelfPermission(this.getApplicationContext(), android.Manifest.permission.RECORD_AUDIO))
        {
        }
        else {
            //提示用户开户权限音频
            String[] perms = {"android.permission.RECORD_AUDIO"};
            ActivityCompat.requestPermissions(this, perms, RESULT_CODE_STARTAUDIO);
        }
    }

2. 持续的补帧策略,防止屏幕不动,没数据下去;

3. 如果需要传部分区域下去,可以用 SmartPublisherOnCaptureVideoClipedRGBAData() 接口;

4. 横竖屏切换,上层无需过问,底层会自动切。

iOS平台RTMP屏幕推送端

对应工程: SmartServiceCameraPublisherV2

注意事项:ReplayKit2 的直播扩展目前是有50M的内存使用限制,超过此限制系统会直接杀死扩展进程,因此 ReplayKit2 上建议推流分辨率和帧率、码率不要太高。

以下是核心processSampleBuffer() 处理,iOS 11.0以上 加入了横竖屏自动切换适配:

- (void)processSampleBuffer:(CMSampleBufferRef)sampleBuffer
                   withType:(RPSampleBufferType)sampleBufferType {
    
    CGFloat cur_memory = [self GetCurUsedMemoryInMB];
    
    if( cur_memory > 20.0f)
    {
        //NSLog(@"processSampleBuffer cur: %.2fM", cur_memory);
        return;
    }
        
    switch (sampleBufferType) {
        case RPSampleBufferTypeVideo:
            {
                if (!CMSampleBufferIsValid(sampleBuffer))
                    return;
                
                NSInteger rotation_degress = 0;
                //11.1以上支持自动旋转
    #ifdef __IPHONE_11_1
                if (UIDevice.currentDevice.systemVersion.floatValue > 11.1) {
                    CGImagePropertyOrientation orientation = ((__bridge NSNumber*)CMGetAttachment(sampleBuffer, (__bridge CFStringRef)RPVideoSampleOrientationKey , NULL)).unsignedIntValue;
                    
                    //NSLog(@"cur org: %d", orientation);
                    
                    switch (orientation)
                    {
                        //竖屏
                        case kCGImagePropertyOrientationUp:{
                            rotation_degress = 0;
                        }
                            break;
                        case kCGImagePropertyOrientationDown:{
                            rotation_degress = 180;
                            break;
                        }
                        case kCGImagePropertyOrientationLeft: {
                            //静音键那边向上 所需转90度
                            rotation_degress = 90;
                        }
                            break;
                        case kCGImagePropertyOrientationRight:{
                            //关机键那边向上 所需转270
                            rotation_degress = 270;
                        }
                            break;
                        default:
                            break;
                    }
                }
    #endif
                
                //NSLog(@"RPSampleBufferTypeVideo");
                if(_smart_publisher_sdk)
                {
                    //[_smart_publisher_sdk SmartPublisherPostVideoSampleBuffer:sampleBuffer];
                    [_smart_publisher_sdk SmartPublisherPostVideoSampleBufferV2:sampleBuffer rotateDegress:rotation_degress];
                }
                
                //NSLog(@"video ts:%.2f", CMTimeGetSeconds(CMSampleBufferGetPresentationTimeStamp(sampleBuffer)));
            }
            break;
        case RPSampleBufferTypeAudioApp:
            //NSLog(@"RPSampleBufferTypeAudioApp");
            if (CMSampleBufferDataIsReady(sampleBuffer) != NO)
            {
                if(_smart_publisher_sdk)
                {
                    NSInteger type = 2;
                    [_smart_publisher_sdk SmartPublisherPostAudioSampleBuffer:sampleBuffer inputType:type];
                }
            }
            //NSLog(@"App ts:%.2f", CMTimeGetSeconds(CMSampleBufferGetPresentationTimeStamp(sampleBuffer)));
            
            break;
        case RPSampleBufferTypeAudioMic:
            //NSLog(@"RPSampleBufferTypeAudioMic");
            if(_smart_publisher_sdk)
            {
                NSInteger type = 1;
                [_smart_publisher_sdk SmartPublisherPostAudioSampleBuffer:sampleBuffer inputType:type];
            }
            //NSLog(@"Mic ts:%.2f", CMTimeGetSeconds(CMSampleBufferGetPresentationTimeStamp(sampleBuffer)));
            
            break;
        default:
            break;
    }
}

大牛直播SDK多路RTSP-RTMP转RTMP官方定制版

视沃科技(大牛直播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小时不间断运行,自带守护进程,转发程序被误关等各种操作后,会自动启动运行,此外,还支持开机自动启动转发或录像。

多路RTSP/RTMP转RTMP推送SDK概览图

官方测试版有1小时限制,拉流超过一小时会自动停止,如需长期运行或直接授权,请联系我们。

image.png

功能说明
  1. 启动程序

支持从守护进程(如需启动转发程序,可点击SmartStreamRelayToolDaemon.exe,守护进程会自动拉起SmartStreamRelayTool.exe,如需关闭转发程序,请先关闭SmartStreamRelayToolDaemon,转发程序方可正常关闭):

image.png
  1. 添加转发项配置信息
image.png

配置说明:

添加配置项:点击页面“添加”按钮:

² 序号:无需关注,系统自动生成;

² 名称:该路转发配置项的描述信息;

² 拉流地址(必须填):需要转发的RTSP或RTMP地址;

² 推流RTMP地址:需要转推的RTMP地址;

² 推流播放地址:需要预览的播放地址;

² 音视频转发选项:可选择之转发音频或视频,亦或同时转发音视频;

² 录像参数配置:可选择录制音频或视频,亦或音视频同时录制,并可设定录像文件前缀。

备注:双击列表配置项,可以查看或编辑配置信息;

删除配置项:选中需要删除的配置数据,点击页面“删除”按钮:

image.png

如何预览推拉流数据?

点击需要预览的“拉流地址”或“推流地址”,URL会同步到左侧预览框,即可实现推拉流数据本地预览。

如不需播放音频,点击“静音”选项即可。

如何转发数据?

  1. 选中需要转发的配置数据项目(如需全部转发,点击全选选项即可);
  2. 点击“拉流”按钮,拉流生效后,页面“流下载速度”会显示当前下载速度;

image.png

  1. 如需停止拉流,选中配置项,点击“停止拉流”即可;
  2. 拉流后,选中需要转发的配置项,点击“推流”按钮;
image.png
  1. 如需停止推流,选中配置项,点击“停止推流”即可;
  2. 如需对某一路录像,在完成“录像全局配置”的前提下,选中配置项,点击“录像”即可;
image.png
  1. 如需停止录像,选中配置项,点击“停止录像”即可。

系统配置:

² 支持程序启动后自动开启转发;

² 支持程序启动后自动开启录像(考虑到Windows平台磁盘读写性能,Windows平台不做多路录像承诺)

² 开机后自动启动(可配置开机自动启动配置名);

image.png

录像全局配置:

² 支持设置录像存储目录;

² 支持设定单个录像文件大小;

² 支持设置文件是否增加日期、时间;

² 支持设置是否音频自动转AAC编码后存储。

image.png
[Windows平台多路RTSP|RTMP转RTMP推送官方定制版]
QQ交流群:

QQ群1(已满):499687479
QQ群2:294891451

Windows平台多路RTSP/RTMP流转RTMP流深度定制版

大牛直播SDK提供的RTSP/RTMP转RTMP流模块,具备低延迟、足够稳定灵活、有状态反馈机制、资源占用低跨平台,以SDK形式提供,给开发者提供更大的便利,已应用于诸多专业第三方公司。

其中:Windows平台对外提供C++ C#接口,Android提供JNI接口封装,iOS提供object c封装。

Windows/Android/iOS RTMP/RTSP多路流媒体转发SDK功能支持:

  1. 支持拉取rtmp流;
  2. 支持拉取rtsp流;
  3. Windows支持本地flv文件转发(支持制定文件位置转发,或转发过程中seek);
  4. 支持本地预览;
  5. 支持转发过程中,实时静音;
  6. 支持转发过程中,切换rtmp/rtsp url,此外,windows平台还支持切换本地flv文件;
  7. 支持录像模块扩展,可边转发边录制,每个文件录制开始结束,均有状态回馈;
  8. 支持内网RTSP网关模块扩展,拉取的流数据,可以流入到内网RTSP网关模块,对外微型RTSP媒体流服务(RTSP url),便于内网访问;
  9. 音频:AAC,并支持拉流后的音频(PCMU/PCMA,Speex等)转AAC后再转发;
  10. 视频:H.264、H.265,支持h265转发(rtsp/rtmp h265转rtmp h265推送)

对应Demo:

  • Windows测试程序:SmartStreamRelayDemo.exe;
  • Windows C++工程:WIN-RelaySDK-CPP-Demo;
  • Windows C#工程:WIN-RelaySDK-CSharp-Demo;
  • Android工程:SmartRelayDemoV2;
  • iOS工程:SmartiOSRelayDemoV2。

鉴于部分公司人员配备不齐或产品开发周期短,一方面想用好的SDK,另一方面,苦于短期内没有好的人力配备完成上层业务逻辑开发,我们联合第三方开发者实现了Windows转发模块深度定制版本:

1. 启动SmartStreamRelayDemo.exe

2. 输入登陆用户名、密码,其中,用户名 admin 密码123456

3. 如需配置拉流和转发RTSP RTMP url,请直接在relayconfig.ini配置

4. 页面编辑配置:
4.1 读取INI文件,获取relayconfig.ini配置项;
4.2 保存INI文件,把上层编辑后的URL配置保存下来,配置后的,可直接保存,以便下次使用
4.3 增加工作任务:页面添加新的转发配置项
4.4 启动工作任务:启动所有转发
4.5 停止工作任务:停止所有转发
4.6 录制:如需录制流,直接点击“功能选项”->“录制”按钮,默认录制到“record”目录
4.7 移除任务:如需删除某一路流,直接点击“功能选项”->“移除”按钮
4.8 拉流预览:预览拉取的RTMP/RTSP流,预览过程中,可实时静音
4.9 推流预览:转发后的RTMP流,可实时预览,亦可实时静音

4. demo版,有一小时显示,每超过一小时,自动停止运行

5. 提供底层SDK授权,上层系统OEM或上层源码授权

更多资料或demo下载:

Github: https://github.com/daniulive/SmarterStreaming

官网:https://www.daniulive.com

QQ群:

如何在IE浏览器播放RTSP或RTMP流(RTSP/RTMP OCX播放控件)

好多开发者一直苦恼于如何在IE浏览器环境下,构建低延迟的RTSP或RTMP播放,对于RTSP流来说,好多公司通常的做法是把RTSP转RTMP,然后分发到RTMP服务器,然后服务器转http-flv出来,浏览器直接播放http-flv流,亦或通过flash控件直接播放RTMP流,还有就是,转hls流出来,缺点是hls流延迟更大。

以上方案未尝不可,如果对播放体验和延迟要求更高,最简单的做法是直接在IE浏览器下加载activex控件。

大牛直播SDK在现有SDK的基础上,扩展了ocx控件,用于IE浏览器下的低延迟RTMP或RTSP播放,不谦虚的说,也可能是行业内功能支持和延迟最好的RTMP和RTSP播放器(支持RTMP/RTSP H.265(hevc)播放)。

页面展示

1. 功能齐全的单画面RTMP流或RTSP流播放:

2. 同时播放4路RTMP流或RTSP流画面:

本地播放和集成说明:

点我下载DEMO

本地播放

DEMO说明

  1. 1_player_ocx.html:单个窗口功能展示。
  2. 4_player_ocx.html:4窗口功能展示。
  3. SmartPlayer.exe:cs架构播放器。

运行网页播放端之前,请确保以管理员权限注册ocx控件:regplayerocx.bat右键–>“以管理员身份运行(A)”,同理,反注册也是需要管理员身份。

注意:大牛直播RTSP/RTMP播放OCX控件只适用于微软IE浏览器。

对应封装接口

	ULONG NT_SetLogPath();
	ULONG NT_Open();
	ULONG NT_Close();
	ULONG NT_StartPlay();
	ULONG NT_StopPlay();
	ULONG NT_SetMute(LONG is_mute);
	ULONG NT_SetURL(LPCTSTR url);
	ULONG NT_SetBuffer(LONG buffer);
	ULONG NT_SetRTSPTcpMode(LONG isUsingTCP);
	ULONG NT_SetRtspTimeout(LONG timeout);
	ULONG NT_SetRtspAutoSwitchTcpUdp(LONG is_auto_switch_tcp_udp);
	ULONG NT_SetFastStartup(LONG isFastStartup);
	ULONG NT_SetLowLatencyMode(LONG mode);
	ULONG NT_SetFlipVertical(LONG is_flip);
	ULONG NT_SetFlipHorizontal(LONG is_flip);
	ULONG NT_SetRotation(LONG degress);
	ULONG NT_SwitchURL(LPCTSTR url);
	ULONG NT_SetCaptureImagePath(LPCTSTR path);
	ULONG NT_CaptureImage();
	ULONG NT_SetRecorderDirectory(LPCTSTR dir);
	ULONG NT_SetRecorderFileMaxSize(ULONG size);
	ULONG NT_NT_SP_RecorderFileNameRuler(ULONG type, LPCTSTR file_name_prefix, LONG append_date, LONG append_time);
	ULONG NT_SetRecorderAudioTranscodeAAC(LONG is_transcode);
	ULONG NT_SetRecorderVideo(LONG is_record_video);
	ULONG NT_SetRecorderAudio(LONG is_record_audio);
	ULONG NT_StartRecorder();
	ULONG NT_StopRecorder();
	ULONG NT_FullScreen();
	void OnSDKEventReceived(ULONG event_id, ULONG param1);
	void OnVideoSizeReceived(ULONG width, ULONG height);

设置LOG存放路径:

ULONG CSmartPlayerActiveXCtrl::NT_SetLogPath(LPCTSTR log_path)

请于NT_Open() 之前调用,代码示例:

var obj = document.getElementById("SmartPlayerActiveX");
				
//如需记录log文件,请确保log路径存在, 如多级目录, 可按照"D:\\Daniulive\\log"类似格式设定, 记录文件名: smart_sdk.log

obj.NT_SetLogPath("D:\\");

接口说明:

1.  ULONG NT_Open();

打开player实例;
2. ULONG NT_Close();

关闭player实例;
3. ULONG NT_StartPlay();

开始播放;
4. ULONG NT_StopPlay();

停止播放;
5. ULONG NT_SetMute(LONG is_mute);

设置实时静音;
6. ULONG NT_SetURL(LPCTSTR url);

设置播放的RTMP或RTSP url;
7. ULONG NT_SetBuffer(LONG buffer);

设置buffer time,缓冲时间,单位:毫秒;
8. ULONG NT_SetRTSPTcpMode(LONG isUsingTCP);

设置RTSP TCP/UDP播放模式;
9. ULONG NT_SetRtspTimeout(LONG timeout);

设置RTSP超时时间;
10. ULONG NT_SetRtspAutoSwitchTcpUdp(LONG is_auto_switch_tcp_udp);

设置是否自动切换TCP/UDP模式;
11. ULONG NT_SetFastStartup(LONG isFastStartup);

设置是否快速启动;
12. ULONG NT_SetLowLatencyMode(LONG mode);

设置是否低延迟模式播放;
13. ULONG NT_SetFlipVertical(LONG is_flip);

设置垂直反转模式图像;
14. ULONG NT_SetFlipHorizontal(LONG is_flip);

设置水平反转图像;
15. ULONG NT_SetRotation(LONG degress);

设置旋转图像,可设定角度:0度 90度 180度 270度;
16. ULONG NT_SwitchURL(LPCTSTR url);

设置快速切换RTSP/RTMP url;
17. ULONG NT_SetCaptureImagePath(LPCTSTR path);

设置快照保存位置;
18. ULONG NT_CaptureImage();

设置实时快照功能;
19. ULONG NT_SetRecorderDirectory(LPCTSTR dir);

设置录像保存位置;
20. ULONG NT_SetRecorderFileMaxSize(ULONG size);

设置单个录像文件最大size,单位:兆;
21. ULONG NT_NT_SP_RecorderFileNameRuler(ULONG type, LPCTSTR file_name_prefix, LONG append_date, LONG append_time);

设置录像文件命名规则:是否需要前缀、是否添加日期、是否添加时间;
22. ULONG NT_SetRecorderAudioTranscodeAAC(LONG is_transcode);

设置录像音频文件是否转AAC后录制,支持PCMA/PCMU/SPEEX转AAC后录制文件;
23. ULONG NT_SetRecorderVideo(LONG is_record_video);

设置是否录制视频;
24. ULONG NT_SetRecorderAudio(LONG is_record_audio);

设置是否录制音频;
25. ULONG NT_StartRecorder();

开始录像;
26. ULONG NT_StopRecorder();

停止录像;
27. ULONG NT_FullScreen();

全屏显示窗口。

事件Event: 

1. void OnSDKEventReceived(ULONG event_id, ULONG param1);

回调网络状态、buffering状态、下载速度等;

事件类型:

<script>
	var NT_EVENT_ID_SMART_PLAYER_SDK = 0x01000000;
	var NT_SP_E_EVENT_ID_BASE = NT_EVENT_ID_SMART_PLAYER_SDK;
	var NT_SP_E_EVENT_ID_CONNECTING				= NT_SP_E_EVENT_ID_BASE | 0x2;	/*连接中*/
	var NT_SP_E_EVENT_ID_CONNECTION_FAILED		= NT_SP_E_EVENT_ID_BASE | 0x3;	/*连接失败*/
	var NT_SP_E_EVENT_ID_CONNECTED				= NT_SP_E_EVENT_ID_BASE | 0x4;	/*已连接*/
	var NT_SP_E_EVENT_ID_DISCONNECTED			= NT_SP_E_EVENT_ID_BASE | 0x5;	/*断开连接*/
	var NT_SP_E_EVENT_ID_NO_MEDIADATA_RECEIVED	= NT_SP_E_EVENT_ID_BASE | 0x8;	/*收不到RTMP数据*/
	var NT_SP_E_EVENT_ID_RTSP_STATUS_CODE       = NT_SP_E_EVENT_ID_BASE | 0xB;  /*rtsp status code上报, 目前只上报401, param1表示status code*/

	/* 接下来请从0x81开始*/
	var NT_SP_E_EVENT_ID_START_BUFFERING = NT_SP_E_EVENT_ID_BASE | 0x81; /*开始缓冲*/
	var NT_SP_E_EVENT_ID_BUFFERING		 = NT_SP_E_EVENT_ID_BASE | 0x82; /*缓冲中, param1 表示百分比进度*/
	var NT_SP_E_EVENT_ID_STOP_BUFFERING  = NT_SP_E_EVENT_ID_BASE | 0x83; /*停止缓冲*/

	var NT_SP_E_EVENT_ID_DOWNLOAD_SPEED  = NT_SP_E_EVENT_ID_BASE | 0x91; /*下载速度, param1表示下载速度,单位是(Byte/s)*/

	var NT_SP_E_EVENT_ID_PLAYBACK_REACH_EOS		= NT_SP_E_EVENT_ID_BASE | 0xa1; /*播放结束, 直播流没有这个事件,点播流才有*/
	var NT_SP_E_EVENT_ID_RECORDER_REACH_EOS		= NT_SP_E_EVENT_ID_BASE | 0xa2; /*录像结束, 直播流没有这个事件, 点播流才有*/
	var NT_SP_E_EVENT_ID_PULLSTREAM_REACH_EOS   = NT_SP_E_EVENT_ID_BASE | 0xa3; /*拉流结束, 直播流没有这个事件,点播流才有*/
	var NT_SP_E_EVENT_ID_DURATION = NT_SP_E_EVENT_ID_BASE | 0xa8; /*视频时长,如果是直播,则不上报,如果是点播的话, 若能从视频源获取视频时长的话,则上报, param1表示视频时长,单位是毫秒(ms)*/
</script>

调用展示:

<script language='javascript' for="SmartPlayerActiveX"  event="OnSDKEventReceived(event_id, param1)">
		// Test 1 - statically load the script (This is the basis for the hack)
		// Works on IE8, IE9, IE10 and IE11
		
		var show_str = "";

		var connection_status = event_id;

		if (connection_status != 0)
		{
			show_str += "链接状态: ";

			if (NT_SP_E_EVENT_ID_CONNECTING == connection_status)
			{
				show_str += "链接中";
			}
			else if (NT_SP_E_EVENT_ID_CONNECTION_FAILED == connection_status)
			{
				show_str += "链接失败";
			}
			else if (NT_SP_E_EVENT_ID_CONNECTED == connection_status)
			{
				show_str += "链接成功";
			}
			else if (NT_SP_E_EVENT_ID_DISCONNECTED == connection_status)
			{
				show_str += "链接断开";
			}
			else if (NT_SP_E_EVENT_ID_NO_MEDIADATA_RECEIVED == connection_status)
			{
				show_str += "收不到数据";
			}
		}

		var download_speed = -1;
		
		if (NT_SP_E_EVENT_ID_DOWNLOAD_SPEED == event_id)
		{
			download_speed = param1;
		}
		
		if (download_speed != -1)
		{
			show_str += "下载速度:" + (download_speed * 8 / 1000).toFixed(0) + "kbps " + (download_speed / 1024).toFixed(0) + "KB/s";
		}
		
		var buffer_status = 0;
		
		if (NT_SP_E_EVENT_ID_START_BUFFERING == event_id
		|| NT_SP_E_EVENT_ID_BUFFERING == event_id
		|| NT_SP_E_EVENT_ID_STOP_BUFFERING == event_id)
		{
			buffer_status = event_id;
		}

		if (buffer_status != 0)
		{
			show_str += "缓冲状态: ";

			if (NT_SP_E_EVENT_ID_START_BUFFERING == buffer_status)
			{
				show_str += "开始缓冲";
			}
			else if (NT_SP_E_EVENT_ID_BUFFERING == buffer_status)
			{
				show_str += "缓冲中" + param1 + "%";
			}
			else if (NT_SP_E_EVENT_ID_STOP_BUFFERING == buffer_status)
			{
				show_str += "结束缓冲";
			}
		}
		
		var EventMsgText = document.getElementById("EventMsg");
					
		EventMsgText.innerHTML = show_str;
	</script>

2. void OnVideoSizeReceived(ULONG width, ULONG height);

回调视频宽高信息。

调用展示:

<script language='javascript' for="SmartPlayerActiveX"  event="OnVideoSizeReceived(width, height)">
	// Test 1 - statically load the script (This is the basis for the hack)
	// Works on IE8, IE9, IE10 and IE11
	
	var VideoResolutionText = document.getElementById("VideoResolution");
				
	VideoResolutionText.innerHTML = width + "*" + height;
</script>

SDK接口调用实例:

播放和录像调用示例:

var is_player_opened = false;
var is_playing = false;
var is_recording = false;

function OpenPlayer()
{			   
	if(is_player_opened)
	{
		return;
	}
	
	var obj = document.getElementById("SmartPlayerActiveX");
	
	//如需记录log文件,请确保log路径存在, 如多级目录, 可按照"D:\\Daniulive\\log"类似格式设定, 记录文件名: smart_sdk.log
	obj.NT_SetLogPath("D:\\");
	
	var ret = obj.NT_Open();
	
	if(ret == 0)
	{   
		//设置TCP/UDP模式
		var rtsp_tcp_mode = document.getElementById("rtspTcpMode").checked ? 1 : 0;
		obj.NT_SetRTSPTcpMode(rtsp_tcp_mode);
		
		//设置RTSP超时时间
		var rtsp_timeout = document.getElementById("rtspTimeout").value;
		obj.NT_SetRtspTimeout(rtsp_timeout);
		
		//设置是否自动切换TCP-UDP模式
		var rtsp_auto_switch_tcp_udp = document.getElementById("rtspAutoSwitchTcpUdp").checked ? 1 : 0;
		obj.NT_SetRtspAutoSwitchTcpUdp(rtsp_auto_switch_tcp_udp);
		
		//设置是否快速启动
		var fast_startup_mode = document.getElementById("fastStartupMode").checked ? 1 : 0;
		obj.NT_SetFastStartup(fast_startup_mode);
		
		//设置需要播放或录像的RTSP/RTMP url
		var url = document.getElementById("playorReocordUrl").value;
		obj.NT_SetURL(url);
		
		//设置实时截图路径(可自行设置或选取系统存在的文件夹), 如多级目录可按照"D:\\Daniulive\\image"类似格式设定
		//var image_path = "D:\\";
		obj.NT_SetCaptureImagePath(image_path);
	
		is_player_opened = true;
	}
}

function ClosePlayer()
{
	if(is_player_opened)
	{
		var obj = document.getElementById("SmartPlayerActiveX");
		obj.NT_Close();
		
		var EventMsgText = document.getElementById("EventMsg");	
		EventMsgText.innerHTML = "";
		
		is_player_opened = false;
	}
}

function OnBnClickedPlay()
{
	if(!isIE())
	{
		alert("非IE浏览器,请用IE打开播放控件..");
		return;
	}
	
	if(!isActiveXInstalled())
	{
		alert("控件未加载,请先加载控件..");
		return;
	}
	
	if(is_playing)
	{
		StopPlayback();
	}
	else
	{
		StartPlayback();
	}
}

//开始播放
function StartPlayback() {
	
	if(!is_playing && !is_recording)
	{
		OpenPlayer();
	}
	
	var obj = document.getElementById("SmartPlayerActiveX");

	//设置是否启用低延迟模式
	var low_latency_mode = document.getElementById("lowlatencyMode").checked ? 1 : 0;
	obj.NT_SetLowLatencyMode(low_latency_mode);
					
	//设置缓冲时间
	var buffer_time = document.getElementById("bufferTime").value;
	obj.NT_SetBuffer(buffer_time);
					
	var ret = obj.NT_StartPlay();
	
	if(ret == 0)
	{
		is_playing = true;	
		
		var playBtnText = document.getElementById("playBtn");
	
		playBtnText.innerHTML = "停止播放";
	}		
}

//停止播放
function StopPlayback() {

	if(!is_playing)
	{
		return;
	}
	
	var obj = document.getElementById("SmartPlayerActiveX");

	obj.NT_StopPlay();
	 
	is_playing = false;
	
	var playBtnText = document.getElementById("playBtn");
	
	playBtnText.innerHTML = "开始播放";
	
	if(!is_recording)
	{
		ClosePlayer();
	}
	
	var VideoResolutionText = document.getElementById("VideoResolution");
			
	VideoResolutionText.innerHTML = "";
}
			
function OnBnClickedRecord()
{
	if(!isIE())
	{
		alert("非IE浏览器,请用IE打开播放控件..");
		return;
	}
	
	if(!isActiveXInstalled())
	{
		alert("控件未加载,请先加载控件..");
		return;
	}

	if(is_recording)
	{
		StopRecorder();
	}
	else
	{
		StartRecorder();
	}
}

//开始录像
function StartRecorder() {
	
	if(!is_playing && !is_recording)
	{
		OpenPlayer();
	}
	
	var obj = document.getElementById("SmartPlayerActiveX");

	//设置实时录像存放路径(可自行设置或选取系统存在的文件夹), 如多级目录可按照"D:\\Daniulive\\rec"类似格式设定
	var rec_dir = "D:\\";
	obj.NT_SetRecorderDirectory(rec_dir);
	
	var rec_max_size = 200;
	obj.NT_SetRecorderFileMaxSize(rec_max_size);
	
	var type = 0;
	var file_name_prefix = "daniulive";
	var append_date = 1;
	var append_time = 1;
	obj.NT_NT_SP_RecorderFileNameRuler(type, file_name_prefix, append_date, append_time);
	
	var is_transcode = 1;
	obj.NT_SetRecorderAudioTranscodeAAC(is_transcode);
	
	var is_record_video = 1;
	obj.NT_SetRecorderVideo(is_record_video);
	
	var is_record_audio = 1;
	obj.NT_SetRecorderAudio(is_record_audio);
	
	var ret = obj.NT_StartRecorder();
	
	if(ret == 0)
	{
		is_recording = true;
		
		var recordBtnText = document.getElementById("recordBtn");
	
		recordBtnText.innerHTML = "停止录像";
	}
}

//停止录像
function StopRecorder() {

	if(!is_recording)
	{
		return;
	}
	
	var obj = document.getElementById("SmartPlayerActiveX");

	obj.NT_StopRecorder();
	
	is_recording = false;
	
	var recordBtnText = document.getElementById("recordBtn");
	
	recordBtnText.innerHTML = "开始录像";
	
	if(!is_playing)
	{
		ClosePlayer();
	}
}

快速切换URL调用示例:

//快速切换播放URL
function SwitchUrl() {

	if(!is_playing)
	{
		return;
	}

	var obj = document.getElementById("SmartPlayerActiveX");

	var switch_url = document.getElementById("playorReocordUrl").value;
	obj.NT_SwitchURL(switch_url);
}

实时静音调用示例:

//实时静音
var is_mute = 1;	
function SetMute() {
	
	var obj = document.getElementById("SmartPlayerActiveX");
	
	obj.NT_SetMute(is_mute);
	
	var muteText = document.getElementById("MuteBtn");
	
	if(is_mute == 1 )
	{
		is_mute = 0;
		muteText.innerHTML = "取消静音";
	}
	else
	{
		is_mute = 1;
		muteText.innerHTML  = "实时静音";
	}
}

视频view垂直反转、水平反转、旋转调用示例:

//垂直反转
var is_flip_vertical = 1;
function SetFlipVertical() {
	
	var obj = document.getElementById("SmartPlayerActiveX");
	
	obj.NT_SetFlipVertical(is_flip_vertical);
	
	var flipVerticalText = document.getElementById("FlipVerticalBtn");
	
	if(is_flip_vertical == 1 )
	{
		is_flip_vertical = 0;
		flipVerticalText.innerHTML = "取消反转";
	}
	else
	{
		is_flip_vertical = 1;
		flipVerticalText.innerHTML  = "垂直反转";
	}
}

//水平反转
var is_flip_horizontal = 1;
function SetFlipHorizontal() {
	
	var obj = document.getElementById("SmartPlayerActiveX");
	
	obj.NT_SetFlipHorizontal(is_flip_horizontal);
	
	var flipHorizontalText = document.getElementById("FlipHorizontalBtn");
	
	if(is_flip_horizontal == 1 )
	{
		is_flip_horizontal = 0;
		flipHorizontalText.innerHTML = "取消反转";
	}
	else
	{
		is_flip_horizontal = 1;
		flipHorizontalText.innerHTML  = "水平反转";
	}
}

//视频view旋转
var rotate_degrees_ = 0;
function SetRotation() {
	
	rotate_degrees_ += 90;
	rotate_degrees_ = rotate_degrees_ % 360;
	
	var obj = document.getElementById("SmartPlayerActiveX");
	
	obj.NT_SetRotation(rotate_degrees_);
	
	var rotateText = document.getElementById("RotateBtn");
					
	if (0 == rotate_degrees_)
	{
		rotateText.innerHTML = "旋转90度";
	}
	else if (90 == rotate_degrees_)
	{
		rotateText.innerHTML = "旋转180度";
	}
	else if (180 == rotate_degrees_)
	{
		rotateText.innerHTML = "旋转270度";
	}
	else if (270 == rotate_degrees_)
	{
		rotateText.innerHTML = "不旋转";
	}
}

实时截图调用示例:

function CaptureImage() {
	var obj = document.getElementById("SmartPlayerActiveX");

	obj.NT_CaptureImage();
}

全屏显示窗口调用示例:

//全屏显示窗口
function FullScreen() {
	var obj = document.getElementById("SmartPlayerActiveX");

	obj.NT_FullScreen();
}

相关资料和测试控件下载:

Github: https://github.com/daniulive/SmarterStreaming

官网:https://www.daniulive.com

QQ群:

RTSP直播推流SDK

技术特点和优势:

  1. 全自研框架,易于扩展,自适应算法让延迟更低、采集编码传输效率更高;
  2. 所有功能以SDK接口形式提供,所有状态,均有event回调,完美支持断网自动重连;
  3. SDK模块化,可和大牛直播播放器SDK组合实现流媒体数据转发、内置轻量级RTSP服务、连麦、一对一互动等场景;
  4. Windows推送端SDK以层级模式提供,开发者可以自行组合数据源(如多摄像头/屏幕/水印叠加);
  5. 支持外部YUV/RGB/H.264/AAC/SPEEX/PCMA/PCMU数据源接入;
  6. 所有参数均可通过SDK接口单独设置,亦可通过默认参数,傻瓜式设置;
  7. RTMP推送、RTSP推送、录像、内置轻量级RTSP服务模块完全分离,可单独使用亦可组合使用;
  8. 业内甚至很难找到效果接近的SDK

具体参见下图“RTSP推流SDK”关联部分:

功能支持:

如不单独说明,系Windows、Android、iOS全平台支持。

  •  [视频采集处理]Windows平台涵盖“Windows视频采集处理SDK”功能;
  •  [音频采集处理]Windows平台涵盖“Windows音频采集处理SDK”功能;
  •  [本地预览]Windows平台支持摄像头/屏幕/合成数据实时预览功能,Android/iOS平台支持本地前后置摄像头预览;
  •  [摄像头反转/旋转]Windows平台支持摄像头水平反转、垂直反转、0°/90°/180°/270°旋转;
  •  [摄像头采集]除常规YUV格式外,Windows平台还支持MJPEG格式的摄像头采集;
  •  [RTSP推流]超低延时的RTSP协议直播推流SDK;
  •  [视频格式]Windows/Android平台支持H.264/H.265编码(Android H.265硬编码),iOS平台支持H.264编码;
  •  [音频格式]Windows/Android/iOS平台支持AAC编码;
  •  [音量调节]Windows/Android平台采集端支持实时音量调节(其中,Windows平台混音模式下支持单独控制麦克风、扬声器音量);
  •  [H.264硬编码]Android/iOS平台支持H.264硬编码;
  •  [H.265硬编码]Android/iOS平台支持H.265硬编码;
  •  [硬编码自适应]Android/iOS平台支持硬编码自适应,如检测到硬编码不支持,自动切换到软编(iOS如H.265硬编,先切换到H.264硬编码,如不支持再尝试H.264软编);
  •  [RTSP鉴权]支持RTSP鉴权推送;
  •  [TCP/UDP模式]支持rtp over udp和rtp over tcp两种传输方式;
  •  [401事件处理]RTSP推送支持401事件上报;
  •  [视频格式]支持H.264/H.265(64位库)编码;
  •  [音频格式]支持AAC编码;
  •  [软硬编码参数配置]支持gop间隔、帧率、bit-rate设置;
  •  [软编码参数配置]支持软编码profile、软编码速度、可变码率设置;
  •  [多实例推送]支持多实例推送(如同时推送屏幕/摄像头和外部数据);
  •  [多分辨率支持]支持摄像头或屏幕多种分辨率设置;
  •  [Windows推屏]Windows平台支持屏幕裁剪、窗口采集、屏幕/摄像头数据合成等多种模式推送;
  •  [事件回调]支持各种状态实时回调;
  •  [水印]Windows平台支持文字水印、png水印、实时遮挡,Android平台支持文字水印、png水印;
  •  [复杂网络处理]支持断网重连等各种网络环境自动适配;
  •  [动态码率]支持根据网络情况自动调整推流码率;
  •  [实时静音]支持推送过程中,实时静音/取消静音;
  •  [实时快照]支持推流过程中,实时快照;
  •  [纯音频推流]支持仅采集音频流并发起推流功能;
  •  [纯视频推流]支持特殊场景下的纯视频推流功能;
  •  [降噪]Windows/Android平台支持降噪处理、自动增益、VAD检测;
  •  [回音消除]Android平台支持实时传递远端PCM数据,方便回音消除处理;
  •  [外部编码前视频数据对接]支持YUV数据对接;
  •  [外部编码前音频数据对接]支持PCM对接;
  •  [外部编码后视频数据对接]支持外部H.264数据对接;
  •  [外部编码后音频数据对接]外部AAC/PCMA/PCMU数据对接;
  •  [推送端休眠设置]Windows平台支持休眠接口(设置成休眠模式后CPU会适当降低);
  •  [扩展录像功能]完美支持和录像SDK组合使用,录像相关功能,可参见”Windows/Android/iOS录像SDK“;
  •  [服务器兼容]支持自建服务器(如Darwin Stream Server)。

对应Demo:

  •  Windows测试程序:SmartPublisherDemo.exe;
  •  Windows C++工程:WIN-PublisherSDK-CPP-Demo;
  •  Android工程:SmartPublisherV2;
  •  iOS工程:SmartiOSPublisherV2。

相关博客:

大牛直播RTMP播放器SDK

技术特点和优势:

在没测试过大牛直播SDK的RTMP播放器之前,你甚至不相信行业内,RTMP播放器延迟可以稳定的做到1秒以内。

无需赘述,全自研内核,行业内一致认可的跨平台RTMP直播播放器SDK,功能齐全、高稳定、超低延迟、超低资源占用,超400家公司明智之选。

具体参见下图“RTMP播放SDK”关联部分:

功能支持:

如不单独说明,系Windows、Linux、Android、iOS全平台支持。

  •  [支持播放协议]高稳定、超低延迟(一秒内,行业内几无效果接近的播放端)、业内领先的RTMP直播播放器SDK;
  •  [多实例播放]支持多实例播放;
  •  [事件回调]支持网络状态、buffer状态等回调;
  •  [视频格式]支持RTMP扩展H.265,H.264;
  •  [音频格式]支持AAC/PCMA/PCMU/Speex;
  •  [H.264/H.265软解码]支持H.264/H.265软解;
  •  [H.264硬解码]Windows/Android/iOS支持特定机型H.264硬解;
  •  [H.265硬解]Windows/Android/iOS支持特定机型H.265硬解;
  •  [H.264/H.265硬解码]Android支持设置Surface模式硬解和普通模式硬解码;
  •  [缓冲时间设置]支持buffer time设置;
  •  [首屏秒开]支持首屏秒开模式;
  •  [低延迟模式]支持低延迟模式设置(公网200~400ms);
  •  [复杂网络处理]支持断网重连等各种网络环境自动适配;
  •  [快速切换URL]支持播放过程中,快速切换其他URL,内容切换更快;
  •  [音视频多种render机制]Android平台,视频:SurfaceView/GLSurfaceView,音频:AudioTrack/OpenSL ES;
  •  [实时静音]支持播放过程中,实时静音/取消静音;
  •  [实时音量调节]支持播放过程中实时调节音量;
  •  [实时快照]支持播放过程中截取当前播放画面;
  •  [只播关键帧]Windows平台支持实时设置是否只播放关键帧;
  •  [渲染角度]支持0°,90°,180°和270°四个视频画面渲染角度设置;
  •  [渲染镜像]支持水平反转、垂直反转模式设置;
  •  [等比例缩放]支持图像等比例缩放绘制(Android设置surface模式硬解模式不支持);
  •  [实时下载速度更新]支持当前下载速度实时回调(支持设置回调时间间隔);
  •  [ARGB叠加]Windows平台支持ARGB图像叠加到显示视频(参看C++的DEMO);
  •  [解码前视频数据回调]支持H.264/H.265数据回调;
  •  [解码后视频数据回调]支持解码后YUV/RGB数据回调;
  •  [解码后视频数据缩放回调]Windows平台支持指定回调图像大小的接口(可以对原视图像缩放后再回调到上层);
  •  [解码前音频数据回调]支持AAC/PCMA/PCMU/SPEEX数据回调;
  •  [音视频自适应]支持播放过程中,音视频信息改变后自适应;
  •  [扩展录像功能]完美支持和录像SDK组合使用(支持RTMP扩展H.265流录制,支持PCMA/PCMU/Speex转AAC后录制,支持设置只录制音频或视频),录像相关功能,可参见”Windows/Android/iOS录像SDK“。

对应Demo:

  •  Windows测试程序:SmartPlayer.exe;
  •  Windows C++工程:WIN-PlayerSDK-CPP-Demo;
  •  Windows C#工程:WIN-PlayerSDK-CSharp-Demo;
  •  Android工程:SmartPlayerV2;
  •  iOS工程:SmartiOSPlayerV2。

相关博客:

大牛直播RTSP播放器SDK

技术特点和优势:

无需赘述,全自研内核,行业内一致认可的跨平台RTSP直播播放器SDK,功能齐全、高稳定、超低延迟,超低资源占用,适用于安防、教育、单兵指挥等行业。

具体参见下图“RTSP播放SDK”关联部分:

功能支持:

如不单独说明,系Windows、Linux、Android、iOS全平台支持。

  •  [支持播放协议]高稳定、超低延迟、业内首屈一指的RTSP直播播放器SDK;
  •  [多实例播放]支持多实例播放;
  •  [事件回调]支持网络状态、buffer状态等回调;
  •  [视频格式]支持H.265、H.264,此外,还支持RTSP MJPEG播放;
  •  [音频格式]支持AAC/PCMA/PCMU;
  •  [H.264/H.265软解码]支持H.264/H.265软解;
  •  [H.264硬解码]Windows/Android/iOS支持特定机型H.264硬解;
  •  [H.265硬解]Windows/Android/iOS支持特定机型H.265硬解;
  •  [H.264/H.265硬解码]Android支持设置Surface模式硬解和普通模式硬解码;
  •  [RTSP模式设置]支持RTSP TCP/UDP模式设置;
  •  [RTSP TCP/UDP自动切换]支持RTSP TCP、UDP模式自动切换;
  •  [RTSP超时设置]支持RTSP超时时间设置,单位:秒;
  •  [RTSP 401认证处理]支持上报RTSP 401事件,如URL携带鉴权信息,会自动处理;
  •  [缓冲时间设置]支持buffer time设置;
  •  [首屏秒开]支持首屏秒开模式;
  •  [复杂网络处理]支持断网重连等各种网络环境自动适配;
  •  [快速切换URL]支持播放过程中,快速切换其他URL,内容切换更快;
  •  [音视频多种render机制]Android平台,视频:surfaceview/OpenGL ES,音频:AudioTrack/OpenSL ES;
  •  [实时静音]支持播放过程中,实时静音/取消静音;
  •  [实时音量调节]支持播放过程中实时调节音量;
  •  [实时快照]支持播放过程中截取当前播放画面;
  •  [只播关键帧]Windows平台支持实时设置是否只播放关键帧;
  •  [渲染角度]支持0°,90°,180°和270°四个视频画面渲染角度设置;
  •  [渲染镜像]支持水平反转、垂直反转模式设置;
  •  [等比例缩放]支持图像等比例缩放绘制(Android设置surface模式硬解模式不支持);
  •  [实时下载速度更新]支持当前下载速度实时回调(支持设置回调时间间隔);
  •  [解码前视频数据回调]支持H.264/H.265数据回调;
  •  [解码后视频数据回调]支持解码后YUV/RGB数据回调;
  •  [解码前音频数据回调]支持AAC/PCMA/PCMU数据回调;
  •  [音视频自适应]支持播放过程中,音视频信息改变后自适应;
  •  [扩展录像功能]完美支持和录像SDK组合使用(支持RTSP H.265流录制,支持PCMA/PCMU转AAC后录制,支持设置只录制音频或视频),录像相关功能,可参见”Windows/Android/iOS录像SDK“。

对应Demo:

  •  Windows测试程序:SmartPlayer.exe;
  •  Windows C++工程:WIN-PlayerSDK-CPP-Demo;
  •  Windows C#工程:WIN-PlayerSDK-CSharp-Demo;
  •  Android工程:SmartPlayerV2;
  •  iOS工程:SmartiOSPlayerV2。

相关博客:

一对一互动SDK

大牛直播一对一互动SDK涵盖Windows、Android和iOS平台。

目前市面上大多一对一互动都是基于WebRTC,缺点如下:

  1. 服务器部署非常复杂,不利于私有部署,在一些私密性高的场景下,无法使用,如公安、市政等体系;
  2. 传输基于UDP,很难保证传输质量,由于UDP是不可靠的传输协议,在复杂的公网网络环境下,各种突发流量、偶尔的传输错误、网络抖动、超时等等都会引起丢包异常,都会在一定程度上影响音视频通信的质量;
  3. 难以应对复杂的互联网环境,如跨区跨运营商、低带宽、高丢包等场景;
  4. 整个框架体系不够灵活,代码复杂度高,行话说的好:从demo到实用,中间还差1万个WebRTC

RTMP/RTSP一对一互动SDK技术特点和优势:

  •  基于官方现有RTMP、RTSP推送、或内置RTSP服务、播放SDK,产品稳定度高,行业内首屈一指的超低延迟特性;
  •  加入噪音抑制、回音消除、自动增益控制等特性,确保通话效果;
  •  采用通用的RTMP和RTSP服务器,如nginx、SRS或 Darwin Stream Server(原生版本),更有利于私有部署;
  •  支持H.264的扩展SEI消息发送机制;
  •  支持H.265编码(Windows/Android/iOS特定机型硬编码)和H.264可变码率设定,换句话说,之前大牛直播SDK推送端支持的功能,都可以同步支持;
  •  支持H.265解码,直播播放器支持的功能,一对一互动模块都可以有选择的支持;
  •  Windows平台支持双流合成大小屏录制;
  •  Windows支持摄像头、屏幕合成、水印等各种组合模式,扩展度高;
  •  适用于应急指挥、教育培训等领域。

对应Demo:

  • 以C#为例,对应SmartEchoCancellation.exe(WIN-EchoCancellation-CSharp-Demo);
  •  Android工程:SmartEchoCancellationV2;
  •  iOS工程:SmartiOSEchoCancellation。

相关博客: