RTSP协议探究

RTSP播放器可广泛应用于对延迟要求比较高的场景下,比如协同操控相关的智能机器人或无人机、实时视频监控、远程视频会议、网络电视等。通过控制信令实现对流媒体数据的远程控制和传输管理。

一、协议基础

  • 定义与目的:RTSP建立并控制一个或多个时间同步流,尽管交错(interleaving)媒体流和控制流是可行的,但RTSP通常不直接参与数据传送,而是作为“网络远程控制”功能存在。
  • 标准规范:遵循IETF RFC 2326等规范,其语法和操作参考了HTTP/1.1。
  • 字符集与编码:采用ISO 10646字符集,使用UTF-8编码。

二、协议特性

  • 有状态协议:与HTTP的无状态特性不同,RTSP是一个有状态的协议,服务器需要维护关于客户端会话的状态信息。
  • 可扩展性:RTSP支持新方法和参数的添加,具有良好的可扩展性。
  • 多服务器兼容:呈现中的各个媒体流可以分布在不同服务器上,客户端会自动与不同服务器建立多个并行的控制会话。
  • 安全性:RTSP重用了Web安全机制,如HTTP的授权机制(basic、digest等)均可直接使用。

三、协议功能

  • 播放控制:客户端可以通过RTSP命令控制媒体流的播放,如播放、暂停、停止、快进和快退等。
  • 媒体描述:RTSP支持通过DESCRIBE方法从服务器获取媒体流的描述信息,通常使用SDP(Session Description Protocol)格式。
  • 会话管理:RTSP支持通过SETUP方法建立会话并准备传输,以及通过TEARDOWN方法结束会话并释放资源。

四、传输机制

  • 传输层协议:RTSP通常基于TCP协议进行交互,默认端口为554。同时,RTSP也可以基于HTTP进行隧道传输(RTSP-over-HTTP tunneling),此时默认端口为8080。
  • 数据流传输:实际的流媒体数据(如音频、视频流)通常通过RTP/RTCP协议进行传输。RTP负责数据传输,RTCP负责传输控制信息。

五、RTSP请求与响应

  • 请求方法:RTSP定义了多种请求方法,包括OPTIONS、DESCRIBE、SETUP、PLAY、PAUSE、TEARDOWN等。这些方法用于实现不同的功能,如查询服务器支持的方法、获取媒体描述、建立会话、控制媒体播放等。
  • 响应状态码:RTSP的响应状态码与HTTP类似,用于表示请求的成功或失败。

六、应用场景

  • 实时流媒体传输:RTSP广泛应用于实时流媒体传输场景,如视频会议、网络摄像头直播、视频点播系统等。
  • 远程控制:RTSP允许客户端远程控制媒体服务器上的流媒体播放,提供了灵活的媒体控制功能。

如何实现RTSP播放器

 本文以大牛直播SDK的Windows平台RTSP直播播放器为例,大概介绍下,如何集成RTSP直播播放能力。

RTSP协议探究和RTSP播放器技术实现_windows rtsp播放器

  • 大牛直播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开发。

集成说明

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目录如下:

  • 32位debug库:debug
  • 32位release库:release
  • 64位debug库:x64\debug
  • 64位release库:x64\release

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

3. 在需要集成的工程,右键->Properties->Application->Assembly name,大牛直播SDK按照APP名称授权,未授权版本,此处请改成“SmartPlayer”,如需授权,可直接联系商务;

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

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

设置授权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;
}

设置日志存放路径

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

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

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

初始化SDK

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

特定机型硬解码检测

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

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

/*
 * 检查是否支持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);

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

设置回调事件

  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,一般播放器无使用需求的话,无需设置;
  2. NT_SP_SetUserDataCallBack:设置用户数据回调,此接口需要和推送端SDK配套使用,用于返回推送端设定的实时用户数据(如时间戳、经纬度等各种扩展指令或信息),如只是单纯使用播放SDK,无需设置;
  3. NT_SP_SetSEIDataCallBack:设置视频SEI数据回调,如只是单纯使用播放SDK,不需要额外处理扩展SEI数据的话,无需设置。

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

设置播放URL

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

设置回调PCM

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

RTMP/RTSP播放参数设置

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

播放前可选设置接口

  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:万能接口, 得到参数, 大多数问题,这些接口都能解决;

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

  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:设置视频画面的填充模式,如填充整个绘制窗口、等比例填充绘制窗口,如不设置,默认填充整个绘制窗口;

开始播放

NT_SP_StartPlay

开始播放RTMP或RTSP流数据。

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:停止录像

实时快照

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

快速切换URL

NT_SP_SwitchURL

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

用户数据回调

NT_SP_SetUserDataCallBack

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

SEI数据回调

NT_SP_SetSEIDataCallBack

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

停止播放

NT_SP_StopPlay

停止播放RTMP或RTSP流数据。

关闭播放实例

NT_SP_Close

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

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

Uninit

NT_SP_UnInit

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