当前位置: 首页 > news >正文

wpf下RTSP|RTMP播放器两种渲染模式实现

技术背景

在这篇blog之前,我提到了wpf下播放RTMP和RTSP渲染的两种方式,一种是通过控件模式,另外一种是直接原生RTSP、RTMP播放模块,回调rgb,然后在wpf下渲染,本文就两种方式做个说明。

技术实现

以大牛直播SDK的Windows平台SmartPlayer为例,我们先说第一种通过控件模式,控件模式,非常简单:可以用picturebox,在MainWindow.xaml 做以下设置:

        <WindowsFormsHost HorizontalAlignment="Left" Height="338" Margin="10,10,0,0" VerticalAlignment="Top" Width="480" Background="Black"><wf:PictureBox x:Name="RealPlayWnd"></wf:PictureBox></WindowsFormsHost>

StartPlayer的时候,调NT_SP_SetRenderWindow,把handler设置下去即可,如果需要硬解码,可以先做硬解码检测,检测支持的话,设置硬解码模式。

       /** nt_player_wrapper.cs* Author: daniusdk.com*/public bool StartPlay(String url, bool is_rtsp_tcp_mode, bool is_mute, bool is_hardware_decorder){if ( is_playing_ )return false;if (!OpenPlayerHandle(url, is_rtsp_tcp_mode, is_mute, is_hardware_decorder))return false;//video resolution callbackvideo_size_call_back_ = new SP_SDKVideoSizeCallBack(SP_SDKVideoSizeHandle);NTSmartPlayerSDK.NT_SP_SetVideoSizeCallBack(player_handle_, IntPtr.Zero, video_size_call_back_);if (render_wnd_ != null){NTSmartPlayerSDK.NT_SP_SetRenderWindow(player_handle_, render_wnd_.Handle);NTSmartPlayerSDK.NT_SP_SetRenderScaleMode(player_handle_, 1);}else if(image_wnd_ != null){//video frame callback (YUV/RGB)//format请参见 NT_SP_E_VIDEO_FRAME_FORMAT,如需回调YUV,请设置为 NT_SP_E_VIDEO_FRAME_FROMAT_I420video_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_);}uint ret = NTSmartPlayerSDK.NT_SP_StartPlay(player_handle_);if ( NTBaseCodeDefine.NT_ERC_OK != ret ){NTSmartPlayerSDK.NT_SP_Close(player_handle_);player_handle_ = IntPtr.Zero;return false;}is_playing_ = true;return true;}

另外一种模式,是通过回调rgb,然后在image上渲染,回调rgb,在StartPlay()已有说明。=,设置回调,选择NT_SP_E_VIDEO_FRAME_FORMAT_RGB32格式,然后处理回调数据即可。

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

处理rgb数据回调的地方,拿到bitmap_source数据,设给image.Source即可:

        public void SDKVideoFrameCallBack(IntPtr handle, UInt32 status, BitmapSource bitmap_source){if (image_wnd_ == null)return;if (player_handle_ == IntPtr.Zero || !is_playing_ || bitmap_source == null)return;image_wnd_.Source = bitmap_source;}

为了便于比较,我们做了个四窗口的demo展示(一路2560*1440,一路1920*1080),上面是通过picturebox控件直接设置handle到原生模块播放,第三第四个窗口知通过image自己绘制:

具体实现如下:

       /** MainWindow.xaml.cs* Author: daniusdk.com*/public MainWindow(){InitializeComponent();if (!InitSDK())return;UIDispatcher = Dispatcher.CurrentDispatcher;player1_ = new nt_player_wrapper(RealPlayWnd, null, UIDispatcher);player1_.EventGetPlayerEventMsg += new DelGetPlayerEventMsg(GetPlayerEventMsgInfo);player1_.EventGetVideoSize += new DelGetVideoSize(GetVideoSize);player2_ = new nt_player_wrapper(RealPlayWnd1, null, UIDispatcher);player2_.EventGetPlayerEventMsg += new DelGetPlayerEventMsg(GetPlayerEventMsgInfo);player2_.EventGetVideoSize += new DelGetVideoSize(GetVideoSize);player3_ = new nt_player_wrapper(null, image_render, UIDispatcher);player3_.EventGetPlayerEventMsg += new DelGetPlayerEventMsg(GetPlayerEventMsgInfo);player3_.EventGetVideoSize += new DelGetVideoSize(GetVideoSize);player4_ = new nt_player_wrapper(null, image_render1, UIDispatcher);player4_.EventGetPlayerEventMsg += new DelGetPlayerEventMsg(GetPlayerEventMsgInfo);player4_.EventGetVideoSize += new DelGetVideoSize(GetVideoSize);}private bool InitSDK(){if (!is_player_sdk_init_){UInt32 isPlayerInited = NT.NTSmartPlayerSDK.NT_SP_Init(0, IntPtr.Zero);if (isPlayerInited != 0){MessageBox.Show("调用NT_SP_Init失败..");return false;}is_player_sdk_init_ = true;}return true;}private void Button_Click_1(object sender, RoutedEventArgs e){if (!player1_.IsPlaying()){player1_.SetBuffer(0);bool is_mute = true;bool is_hardware_decoder = true;if (!player1_.StartPlay("rtsp://admin:daniulive12345@192.168.0.120:554/h264/ch1/main/av_stream", false, is_mute, is_hardware_decoder))return;btn_playback1.Content = "停止播放";}else{player1_.StopPlay();btn_playback1.Content = "开始播放";}}private void Button_Click_2(object sender, RoutedEventArgs e){if (!player2_.IsPlaying()){player2_.SetBuffer(0);bool is_mute = true;bool is_hardware_decoder = true;if (!player2_.StartPlay("rtsp://admin:admin123456@192.168.0.121:554/cam/realmonitor?channel=1&subtype=0", false, is_mute, is_hardware_decoder))return;btn_playback2.Content = "停止播放";}else{player2_.StopPlay();btn_playback2.Content = "开始播放";}}private void Button_Click_3(object sender, RoutedEventArgs e){if (!player3_.IsPlaying()){player3_.SetBuffer(0);bool is_mute = true;bool is_hardware_decoder = true;if (!player3_.StartPlay("rtsp://admin:daniulive12345@192.168.0.120:554/h264/ch1/main/av_stream", false, is_mute, is_hardware_decoder))return;btn_playback3.Content = "停止播放";}else{player3_.StopPlay();btn_playback3.Content = "开始播放";}}private void Button_Click_4(object sender, RoutedEventArgs e){if (!player4_.IsPlaying()){player4_.SetBuffer(0);bool is_mute = true;bool is_hardware_decoder = true;if (!player4_.StartPlay("rtsp://admin:admin123456@192.168.0.121:554/cam/realmonitor?channel=1&subtype=0", false, is_mute, is_hardware_decoder))return;btn_playback4.Content = "停止播放";}else{player4_.StopPlay();btn_playback4.Content = "开始播放";}}

关闭窗口的时候,记得调用停止播放逻辑,所有实例关闭后,调用NT_SP_UnInit():

        protected override void OnClosing(System.ComponentModel.CancelEventArgs e){if (MessageBox.Show("确定要关闭窗口吗?", "确认", MessageBoxButton.YesNo) != MessageBoxResult.Yes){// 如果用户选择“否”,取消关闭e.Cancel = true;}if (player1_.IsPlaying()){player1_.StopPlay();}player1_.Dispose();if (player2_.IsPlaying()){player2_.StopPlay();}player2_.Dispose();if (player3_.IsPlaying()){player3_.StopPlay();}player3_.Dispose();if (player4_.IsPlaying()){player4_.StopPlay();}player4_.Dispose();if (is_player_sdk_init_){NTSmartPlayerSDK.NT_SP_UnInit();is_player_sdk_init_ = false;}    base.OnClosing(e);}

总结

wpf下实现低延迟的RTSP或RTMP播放,以上两种模式都可以尝试看,都不麻烦,如果想更灵活,可以采用回调rgb然后自己绘制的模式,如果想更省事,那么直接picturebox控件handle设置下去,底层自己绘制,以上是大概的实现逻辑,感兴趣的开发者,或有这方面技术诉求的,有问题可以单独跟我沟通。

http://www.lryc.cn/news/338872.html

相关文章:

  • Element-UI 自定义-下拉框选择年份
  • 二叉树的链式存储
  • [计算机效率] 鼠标手势工具:WGestures(解放键盘的超级效率工具)
  • Linux useradd命令教程:如何创建新的用户账户(附实例详解和注意事项)
  • 基于ollama搭建本地chatGPT
  • C++11 数据结构3 线性表的循环链式存储,实现,测试
  • 初识DOM
  • 计算机视觉实验五——图像分割
  • 移动Web学习06-移动端适配Less预处理器项目案例
  • LangChain-25 ReAct 让大模型自己思考和决策下一步 AutoGPT实现途径、AGI重要里程碑
  • 24/04/15总结
  • vue3、vue2中nextTick源码解析
  • 【氮化镓】GaN HEMTs结温和热阻测试方法
  • c++11 标准模板(STL)本地化库 - 平面类别(std::codecvt) - 在字符编码间转换,包括 UTF-8、UTF-16、UTF-32 (四)
  • 【状态压缩 容斥原理 组合数学】100267. 单面值组合的第 K 小金额
  • .net框架和c#程序设计第三次测试
  • 架构师系列-搜索引擎ElasticSearch(五)- 索引设计
  • kafka ----修改log4j、jmx、jvm参数等
  • Python 全栈 Web 应用模板:成熟架构,急速开发 | 开源日报 No.223
  • STM32之DHT11温湿度传感器
  • paddle ocr
  • Xcode 15.0 新 #Preview 预览让 SwiftUI 界面调试更加悠然自得
  • 【VS2019】x64 Native Tools Command Prompt for Vs 2019使用conda命令进入环境
  • 网络篇09 | 运输层 udp
  • vim相关指令
  • STM32常见调试工具介绍
  • 简历上写熟悉Linux下常用命令?直接寄
  • 【设计模式】4、prototype 原型模式
  • ES6 关于Class类的继承 extends(2024-04-10)
  • 边缘计算【智能+安全检测】系列教程--使用OpenCV+GStreamer实现真正的硬解码,完全消除马赛克