低延迟播放超高分辨率(4K+)帧率(50帧+)RTSP|RTMP流技术探讨和实现

本文涉及的产品
视觉智能开放平台,视频资源包5000点
视觉智能开放平台,图像资源包5000点
视觉智能开放平台,分割抠图1万点
简介: 为满足安检等场景需求,需支持4K+分辨率与50帧以上的高帧率视频流播放。实现这一目标的关键步骤包括:确保视频源支持高帧率输出、选用高性能RTSP/RTMP播放器以处理高负载视频解码、采用硬件解码以降低CPU负担、保证充足的网络带宽以维持流畅播放并控制延迟、合理配置播放器缓冲策略以适应网络波动、进行性能监控与调试以优化播放效果,以及确保播放器在多平台上的良好兼容性和表现。例如,大牛直播SDK的SmartPlayer在不同平台上实现了稳定且低延迟(150-300ms)的播放体验,支持多种视频和音频格式及多种功能,如多实例播放、事件回调、视频快照等。

技术背景

我们在对接RTSP、RTMP推拉流播放的时候,开发者提到这样的技术诉求,他们在用于安检等场景的时候,采集分辨率甚至需要4K+,帧率需要达到50帧以上,码率也非常高,这就对推流和播放模块,提出了更高的要求。

以播放端为例,如果需要播放50帧以上高帧率高码率高分辨率的RTSP或RTMP流,以下是一些关键的步骤和考虑因素:

1. 确保视频源支持高帧率

  • 视频源设备:确保你的视频源设备(如摄像头、摄像机等)能够输出50帧的视频流。
  • 编码器设置:如果视频流在传输前经过编码器,需要确保编码器支持并配置为输出50帧的视频。

2. 选择合适的RTSP播放器

  • 播放器性能:选择一款性能强大的RTSP|RTMP播放器,能够处理高帧率视频流的解码和渲染。
  • 硬解码支持:超高帧率码率和分辨率的RTSP|RTMP视频流播放,由于解码非常耗费性能,建议采用硬解码模式。

3. 网络条件

  • 带宽:高帧率视频流需要更高的带宽来保持流畅的播放。确保你的网络环境能够提供足够的带宽来支持50帧的视频流。
  • 延迟:低延迟是直播和实时视频传输的重要要求。

4. 播放器配置

  • 软硬解码设置:根据设备性能,选择软解或硬解码。
  • 缓冲设置:合理配置播放器的缓冲策略,以应对网络波动和视频流中的突发情况。

5. 优化和调试

  • 性能监控:使用性能监控工具来评估播放器的性能,包括CPU使用率、内存占用和帧率等。
  • 调试工具:利用播放器的调试工具来诊断和解决可能出现的问题,如解码错误、同步问题等。

6. 跨平台兼容性

  • 多平台支持:如果你需要在不同的平台上播放高帧率视频(如Windows、Linux、Android、iOS等),需要确保播放器在这些平台上都有良好的表现和兼容性。

技术实现

以大牛直播SDK的SmartPlayer为例,目前实现的功能如下,如不单独介绍,Windows、Linux、Android、iOS均支持,现场测试,超过1080p,50帧以上,依然可以达到150-300ms延迟:

image.gif

  • [支持播放协议]高稳定、超低延迟、业内首屈一指的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组合使用。

对应Demo:

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

以Winodws平台为例,开始播放实现如下:

/*
 * SmartPlayerForm.cs
 * Author:daniusdk.com
 * QQ 89030985
 */
private void btn_play_Click(object sender, EventArgs e)
{
    if (btn_play.Text == "播放")
    {
        if (!is_recording_)
        {
            if (!InitCommonSDKParam())
            {
                MessageBox.Show("设置参数错误!");
                return;
            }
        }
        //video resolution callback
        video_size_call_back_ = new SP_SDKVideoSizeCallBack(SP_SDKVideoSizeHandle);
        NTSmartPlayerSDK.NT_SP_SetVideoSizeCallBack(player_handle_, IntPtr.Zero, video_size_call_back_);
        bool is_support_d3d_render = false;
        Int32 in_support_d3d_render = 0;
        if (NT.NTBaseCodeDefine.NT_ERC_OK == NTSmartPlayerSDK.NT_SP_IsSupportD3DRender(player_handle_, playWnd.Handle, ref in_support_d3d_render))
        {
            if (1 == in_support_d3d_render)
            {
                is_support_d3d_render = true;
            }
        }
        //is_support_d3d_render = false;
        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_);
        }
        user_data_call_back_ = new SP_SDKUserDataCallBack(SDKUserDataCallBack);
        NTSmartPlayerSDK.NT_SP_SetUserDataCallBack(player_handle_, IntPtr.Zero, user_data_call_back_);
        if (btn_check_add_osd.Checked)
        {
            DrawOSD("叠加字符展示");
        }
        else
        {
            DrawOSD(null);
        }
        UInt32 ret_start = NTSmartPlayerSDK.NT_SP_StartPlay(player_handle_);
        if (ret_start != 0)
        {
            MessageBox.Show("播放失败..");
            return;
        }
        //转发相关,具体参见转发demo
        /*
        pull_stream_video_data_call_back_ = new SP_SDKPullStreamVideoDataCallBack(OnVideoDataHandle);
        pull_stream_audio_data_call_back_ = new SP_SDKPullStreamAudioDataCallBack(OnAudioDataHandle);
        NTSmartPlayerSDK.NT_SP_SetPullStreamVideoDataCallBack(pull_handle_, IntPtr.Zero, pull_stream_video_data_call_back_);
        NTSmartPlayerSDK.NT_SP_SetPullStreamAudioDataCallBack(pull_handle_, IntPtr.Zero, pull_stream_audio_data_call_back_);
        UInt32 ret = NTSmartPlayerSDK.NT_SP_StartPullStream(pull_handle_);
         */
        btn_capture_image.Enabled = true;
        is_playing_ = true;
        btn_play.Text = "停止";
    }
    else
    {
        StopPlayback();
    }
    
}

image.gif

停止播放:

private void StopPlayback()
{
    if (player_handle_ == IntPtr.Zero)
    {
        return;
    }
    if (is_playing_)
    {
        NTSmartPlayerSDK.NT_SP_StopPlay(player_handle_);
        is_playing_ = false;
        //playWnd.Invalidate();   //清空最后一帧数据,如不加,默认保留最后一帧画面
    }
    if (cur_video_frame_.plane0_ != IntPtr.Zero)
    {
        Marshal.FreeHGlobal(cur_video_frame_.plane0_);
        cur_video_frame_.plane0_ = IntPtr.Zero;
    }
    btn_full_screen_.Enabled = false;
    btn_capture_image.Enabled = false;
    textBox_resolution.Text = "";
    btn_play.Text = "播放";
    lable_cur_status_txt.Text = "";
    edit_player_msg_.Text = "";
    DrawOSD(null);
}

image.gif

如需要实时快照:

private void btn_capture_image_Click(object sender, EventArgs e)
{
    if ( String.IsNullOrEmpty(capture_image_path_) )
    {
        MessageBox.Show("请先设置保存截图文件的目录! 点击截图左边的按钮设置!");
        return;
    }
    if ( player_handle_ == IntPtr.Zero )
    {
        return;
    }
    if ( !is_playing_)
    {
        MessageBox.Show("请在播放状态下截图!");
        return;
    }
    String name = capture_image_path_ + "\\" +  DateTime.Now.ToString("hh-mm-ss") + ".png";
    byte[] buffer1 = Encoding.Default.GetBytes(name);
    byte[] buffer2 = Encoding.Convert(Encoding.Default, Encoding.UTF8, buffer1, 0, buffer1.Length);
    byte[] buffer3 = new byte[buffer2.Length + 1];
    buffer3[buffer2.Length] = 0;
    Array.Copy(buffer2, buffer3, buffer2.Length);
    IntPtr file_name_ptr = Marshal.AllocHGlobal(buffer3.Length);
    Marshal.Copy(buffer3, 0, file_name_ptr, buffer3.Length);
    capture_image_call_back_ = new SP_SDKCaptureImageCallBack(SDKCaptureImageCallBack);
    UInt32 ret = NTSmartPlayerSDK.NT_SP_CaptureImage(player_handle_, file_name_ptr, IntPtr.Zero, capture_image_call_back_);
    Marshal.FreeHGlobal(file_name_ptr);
    if (NT.NTBaseCodeDefine.NT_ERC_OK == ret)
    {
        // 发送截图请求成功
    }
    else if ((UInt32)NT.NTSmartPlayerDefine.SP_E_ERROR_CODE.NT_ERC_SP_TOO_MANY_CAPTURE_IMAGE_REQUESTS == ret)
    {
        // 通知用户延时
        MessageBox.Show("Too many capture image requests!");
    }
    else
    {
        // 其他失败
    }
}

image.gif

如果需要实时录像:

private void btn_record_Click(object sender, EventArgs e)
{
    if (player_handle_ == IntPtr.Zero)
        return;
    if (btn_record.Text == "录像")
    {
        if (!is_rec_video_ && !is_rec_audio_)
        {
            MessageBox.Show("音频录制选项和视频录制选项至少需要选择一个!");
            return;
        }
        if (!is_playing_)
        {
            if (!InitCommonSDKParam())
            {
                MessageBox.Show("设置参数错误!");
                return;
            }
        }
        NTSmartPlayerSDK.NT_SP_SetRecorderVideo(player_handle_, is_rec_video_ ? 1 : 0);
        NTSmartPlayerSDK.NT_SP_SetRecorderAudio(player_handle_, is_rec_audio_ ? 1 : 0);
        UInt32 ret = NTSmartPlayerSDK.NT_SP_SetRecorderDirectoryW(player_handle_, rec_dir_);
        if (NT.NTBaseCodeDefine.NT_ERC_OK != ret)
        {
            MessageBox.Show("设置录像目录失败");
            return;
        }
        NTSmartPlayerSDK.NT_SP_SetRecorderFileMaxSize(player_handle_, max_file_size_);
        NT_SP_RecorderFileNameRuler rec_name_ruler = new NT_SP_RecorderFileNameRuler();
        rec_name_ruler.type_ = 0;
        rec_name_ruler.file_name_prefix_ = rec_name_file_prefix_;
        rec_name_ruler.append_date_ = is_append_date_ ? 1 : 0;
        rec_name_ruler.append_time_ = is_append_time_ ? 1 : 0;
        NTSmartPlayerSDK.NT_SP_SetRecorderFileNameRuler(player_handle_, ref rec_name_ruler);
        record_call_back_ = new SP_SDKRecorderCallBack(SDKRecorderCallBack);
        NTSmartPlayerSDK.NT_SP_SetRecorderCallBack(player_handle_, IntPtr.Zero, record_call_back_);
        NTSmartPlayerSDK.NT_SP_SetRecorderAudioTranscodeAAC(player_handle_, is_audio_transcode_aac_ ? 1 : 0);
        if (NT.NTBaseCodeDefine.NT_ERC_OK != NTSmartPlayerSDK.NT_SP_StartRecorder(player_handle_))
        {
            MessageBox.Show("录像失败!");
            return;
        }
        btn_record.Text = "停止录像";
        is_recording_ = true;
    }
    else
    {
        StopRecorder();
    }
}

image.gif

停止录像:

private void StopRecorder()
{
    if (player_handle_ == IntPtr.Zero)
    {
        return;
    }
    NTSmartPlayerSDK.NT_SP_StopRecorder(player_handle_);
    btn_record.Text = "录像";
    is_recording_ = false;
}

image.gif

总结

RTSP|RTMP播放器,如果需要低延迟的播放50帧以上的高帧率码率的数据,需要有好的解码性能、做好音视频的同步处理,确保播放器的每个环节可控。才可以达到高稳定、低延迟的播放体验,感兴趣的开发者,可以单独跟我沟通讨论。

相关文章
|
3月前
|
监控 Windows
Windows平台RTSP|RTMP播放器如何实时调节音量
我们在做Windows平台RTSP、RTMP播放器的时候,有这样的技术需求,特别是多路监控的时候,并不是每一路audio都需要播放出来的,所以,这时候,需要有针对音量调节的设计
|
Web App开发 编解码 前端开发
VUE网页实时播放海康、大华摄像头RTSP视频流完全方案,300毫秒延迟,支持H.265、可多路同时播放
在遍地都是摄像头的今天,往往需要在各种信息化、数字化、可视化B/S系统中集成实时视频流播放等功能,海康、大华、华为等厂家摄像头或录像机等设备一般也都遵循监控行业标准,支持国际标准的主流传输协议RTSP输出,而Chrome、Firefox、Edge等新一代浏览器从2015年开始取消了NPAPI插件技术支持导致RTSP流无法直接原生播放了
3097 0
|
4月前
|
编解码 网络协议 开发工具
Android平台如何实现多路低延迟RTSP|RTMP播放?
本文档详细介绍了大牛直播SDK在Android平台上实现RTSP与RTMP流媒体播放及录像功能的技术细节。早在2015年,SDK的第一版就已经支持了多实例播放,并且通过简单的实例封装就能轻松实现。文档中提供了代码示例,展示了如何开启播放、停止播放以及开始和停止录像等功能。此外,SDK还提供了丰富的配置选项,例如设置录像目录、文件大小限制、转码选项等。总结部分列出了该SDK的关键特性,包括但不限于高稳定性和低延迟的播放能力、多实例支持、事件回调、硬解码支持、网络状态监控以及复杂的网络环境处理等。这些功能使得SDK能够应对各种应用场景,特别是在对延迟和稳定性有极高要求的情况下表现优异。
|
4月前
|
编解码 网络协议 vr&ar
Android平台下VR头显如何低延迟播放4K以上超高分辨率RTSP|RTMP流
这段内容讲述了VR头显中实现高分辨率视频播放的技术背景与实现方法,并强调了其重要性。高分辨率对于提升VR体验至关重要,它能提供更清晰的画面、增强沉浸感、补偿透镜放大效应,并维持宽广视场角下的图像质量。文中提到的大牛直播SDK具备极低的延迟(200-400ms),支持多种协议与格式,并具有丰富的功能特性,如多实例播放、事件回调、视频及音频格式支持等。此外,提供了基于Unity的播放器示例代码,展示了如何配置播放参数并开始播放。最后,作者指出此类技术在远程控制、虚拟仿真等应用场景中的重要意义。
|
存储 Cloud Native Linux
音视频 ffplay播放控制
音视频 ffplay播放控制
|
存储 编解码 缓存
海康摄像头开发笔记(一):连接防爆摄像头、配置摄像头网段、设置rtsp码流、播放rtsp流、获取rtsp流、调优rtsp流播放延迟以及录像存储
Hik防爆摄像头录像,因为防爆摄像头会有对应的APP软件,与普通的网络摄像头和球机不一样,默认认为它不可以通过web网页配置,所以弄了个来实测确认。经测试实际上也是可以通过web网页配置(与网络摄像头基本是一致的,在码流方面可能会有些不一样),然后提取rtsp流的,界面与球机无异,只是没有球机的云台控制功能,但是界面上也是有的。
海康摄像头开发笔记(一):连接防爆摄像头、配置摄像头网段、设置rtsp码流、播放rtsp流、获取rtsp流、调优rtsp流播放延迟以及录像存储
|
编解码 开发工具 开发者
如何支持RTSP播放H.265(HEVC)流
随着H.265的普及,越来越多的开发者希望大牛直播SDK能支持低延迟的RTSP H.265播放,并分享相关经验: 实现思路: 对rtsp来说,要播放h265只要正确解析sdp和rtp包即可. 下面对这些相关内容做一些介绍.
452 1
|
开发工具 图形学 Android开发
如何在Unity3d平台下低延迟播放RTMP或RTSP流
随着VR类、工业仿真、智慧城市等场景的快速发展,开发者对Unity3d低延迟的直播需求量越来越大,前两年,大牛直播SDK发布了Windows平台、Android平台和iOS平台的Unity3d RTMP和RTSP的播放,好多公司用起来体验都非常好,以下介绍大概实现流程。
251 0
|
编解码 Android开发 iOS开发
如何推送和播放RTMP H265流 (RTMP HEVC)
rtmp 播放h265 首先要扩展flv协议,国内常用扩展方式是给flv的videotag.codecid增加一个新类型(12)来表示h265(hevc),其他和h264规则差不多,另外和h264不同的地方是要解析HEVCDecoderConfigurationRecord,从HEVCDecoderConfigurationRecord中解析出vps, sps, pps. 有了vps, sps, pps, 就可以解码。
427 0
|
vr&ar 开发工具 图形学
如何在VR头显端实现低延迟的RTSP或RTMP播放
VR(虚拟现实技术)给我们带来身临其境的视觉体验,广泛的应用于城市规划、教育培训、工业仿真、房地产、水利电力、室内设计、文旅、军事等众多领域
109 0

热门文章

最新文章