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设置下去,底层自己绘制,以上是大概的实现逻辑,感兴趣的开发者,或有这方面技术诉求的,有问题可以单独跟我沟通。
相关文章:
wpf下RTSP|RTMP播放器两种渲染模式实现
技术背景 在这篇blog之前,我提到了wpf下播放RTMP和RTSP渲染的两种方式,一种是通过控件模式,另外一种是直接原生RTSP、RTMP播放模块,回调rgb,然后在wpf下渲染,本文就两种方式做个说明。 技术实现 以大牛直…...
Element-UI 自定义-下拉框选择年份
1.实现效果 场景表达: 默认展示当年的年份,默认展示前7年的年份 2.实现思路 创建一个新的Vue组件。 使用<select>元素和v-for指令来渲染年份下拉列表。 使用v-model来绑定选中的年份值。 3.实现代码展示 <template><div><el-…...
二叉树的链式存储
二叉树是一种非常重要的数据结构,它能够高效地进行数据的插入、删除和查找操作。二叉树的每个节点最多有两个子节点,分别是左子节点和右子节点。二叉树可以采用多种不同的存储方式来实现,其中链式存储是最为直观和常用的一种方法。本文将深入…...
[计算机效率] 鼠标手势工具:WGestures(解放键盘的超级效率工具)
3.22 鼠标手势工具:WGestures 通过设置各种鼠标手势和操作进行绑定。当用户通过鼠标绘制出特定的鼠标手势后就会触发已经设置好的操作。有点像浏览器中的鼠标手势,通过鼠标手势操纵浏览器做一些特定的动作。这是一款强大的鼠标手势工具,可以…...
Linux useradd命令教程:如何创建新的用户账户(附实例详解和注意事项)
Linux useradd命令介绍 useradd是Linux中用于添加用户账户的命令。它可以用于创建新的用户,并可以配合不同的选项来指定用户的主目录、UID、GID、组等信息。 Linux useradd命令适用的Linux版本 useradd命令在大多数Linux发行版中都可以使用,包括但不限…...
基于ollama搭建本地chatGPT
ollama帮助我们可以快速在本地运行一个大模型,再整合一个可视化页面就能构建一个chatGPT,可视化页面我选择了chat-ollama(因为它还能支持知识库,可玩性更高),如果只是为了聊天更推荐chatbox 部署步骤 下载…...
C++11 数据结构3 线性表的循环链式存储,实现,测试
上一节课,我们学了线性表 单向存储结构(也就是单链表),这个是企业常用的技术,且是后面各种的基本,一定要牢牢掌握,如果没有掌握,下面的课程会云里雾里。 一 ,循环链表 1…...
初识DOM
目录 前言: 1.初识DOM: 1.1DOM树: 1.2节点(Node): 1.2.1元素节点: 1.2.2属性节点: 1.2.3文本节点: 1.3Document对象: 2.操作网页元素: 2.1找出元素: 2.1.1document.getElementById(id)࿱…...
计算机视觉实验五——图像分割
计算机视觉实验五——图像分割 一、实验目标二、实验内容1.了解图割操作,实现用户交互式分割,通过在一幅图像上为前景和背景提供一些标记或利用边界框选择一个包含前景的区域,实现分割①图片准备②代码③运行结果④代码说明 2.采用聚类法实现…...
移动Web学习06-移动端适配Less预处理器项目案例
项目目标:实现在不同宽度设备中等比缩放的网页效果 Less代码 import ./base; import ./normalize;// 变量: 存储37.5 rootSize: 37.5rem; *{margin: 0;padding: 0; } body {background-color: #F0F0F0; }// 主体内容 .main {// padding-bottom: (50 / 37.5rem);pa…...
LangChain-25 ReAct 让大模型自己思考和决策下一步 AutoGPT实现途径、AGI重要里程碑
背景介绍 大模型ReAct(Reasoning and Acting)是一种新兴的技术框架,旨在通过逻辑推理和行动序列的构建,使大型语言模型(LLM)能够达成特定的目标。这一框架的核心思想是赋予机器模型类似人类的推理和行动能…...
24/04/15总结
多线程: 线程 线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位 并发:在同一时刻,有多个指令在单个cpu上交替执行 并行:在同一时刻,有多个指令在多个cpu上同时执行 多线程的实现方式 1.继承…...
vue3、vue2中nextTick源码解析
nexttick是啥 nextTick是Vue提供的一个全局API,由于Vue的异步更新策略导致我们对数据的修改不会更新,如果此时想要获取更新后的Dom,就需要使用这个方法. vue的异步更新策略意思是如果数据变化,vue不会立刻更新dom,而是开启一个队列,把组件更…...
【氮化镓】GaN HEMTs结温和热阻测试方法
文章《Temperature rise detection in GaN high-electron-mobility transistors via gate-drain Schottky junction forward-conduction voltages》,由Xiujuan Huang, Chunsheng Guo, Qian Wen, Shiwei Feng, 和 Yamin Zhang撰写,发表在《Microelectroni…...
c++11 标准模板(STL)本地化库 - 平面类别(std::codecvt) - 在字符编码间转换,包括 UTF-8、UTF-16、UTF-32 (四)
本地化库 本地环境设施包含字符分类和字符串校对、数值、货币及日期/时间格式化和分析,以及消息取得的国际化支持。本地环境设置控制流 I/O 、正则表达式库和 C 标准库的其他组件的行为。 平面类别 在字符编码间转换,包括 UTF-8、UTF-16、UTF-32 std::…...
【状态压缩 容斥原理 组合数学】100267. 单面值组合的第 K 小金额
本文涉及知识点 状态压缩 容斥原理 组合数学 二分查找算法合集 LeetCode100267. 单面值组合的第 K 小金额 给你一个整数数组 coins 表示不同面额的硬币,另给你一个整数 k 。 你有无限量的每种面额的硬币。但是,你 不能 组合使用不同面额的硬币。 返回…...
.net框架和c#程序设计第三次测试
目录 一、测试要求 二、实现效果 三、实现代码 一、测试要求 二、实现效果 数据库中的内容: 使用数据库中的账号登录: 若不是数据库中的内容: 三、实现代码 login.aspx文件: <% Page Language"C#" AutoEventW…...
架构师系列-搜索引擎ElasticSearch(五)- 索引设计
索引创建后,要非常谨慎,创建不好后面会出现各种问题。 索引设计的重要性 索引创建后,索引分片只能通过_split和_shrink 接口对其进行成倍的增加和缩减。 ES的数据是通过_routing分配到各个分片上的,所以本质上不推荐区改变索引的…...
kafka ----修改log4j、jmx、jvm参数等
1、修改log4j 日志路径 在kafka-run-class.sh文件中修改如下配置,将 LOG_DIR变量指定为自己想要存储的路径 # Log directory to use if [ "x$LOG_DIR" "x" ]; thenLOG_DIR"$base_dir/logs" fi2、修改jmx参数 在kafka-run-class.s…...
Python 全栈 Web 应用模板:成熟架构,急速开发 | 开源日报 No.223
tiangolo/full-stack-fastapi-template Stars: 15.6k License: MIT full-stack-fastapi-template 是一个现代化的全栈 Web 应用模板。 使用 FastAPI 构建 Python 后端 API。使用 SQLModel 进行 Python SQL 数据库交互(ORM)。Pydantic 用于数据验证和设…...
轻量级GraphRAG框架nano-graphrag:模块化设计与实践指南
1. 项目概述:一个为开发者而生的轻量级GraphRAG实现 如果你正在寻找一个能够快速上手、代码清晰、易于二次开发的GraphRAG(图增强检索生成)框架,那么 nano-graphrag 很可能就是你需要的那个工具。GraphRAG这个概念,简…...
详解 Deepsec:Vercel 开源 AI 代码安全防护工具的技术架构与实现原理
摘要在 AI 大模型深度融入软件开发全链路的今天,代码安全防护正面临 “复杂逻辑漏洞难发现、传统工具误报率高、源码隐私保护难” 三重核心挑战。Vercel 开源的 Deepsec 作为一款Agent 驱动的本地化 AI 安全防护工具,跳出传统 SAST(静态应用安…...
从学生成绩表到销售报表:手把手教你用ag-grid列组/行组构建复杂业务表格
企业级销售报表实战:用ag-grid行组与列组构建动态分析系统 当业务数据从Excel迁移到前端可视化系统时,开发团队常面临多维分析的挑战。某零售企业曾因无法实时查看"华东区→浙江省→杭州市"三级维度下的季度销售趋势,导致错失库存调…...
高性能服务架构缓存设计:Redis+Caffeine
👉 这是一个或许对你有用的社群🐱 一对一交流/面试小册/简历优化/求职解惑,欢迎加入「芋道快速开发平台」知识星球。下面是星球提供的部分资料: 《项目实战(视频)》:从书中学,往事上…...
深入理解 Tool Use 机制:AI Agent Harness Engineering 如何调用外部工具
深入理解 Tool Use 机制:AI Agent Harness Engineering 如何调用外部工具 引言 背景介绍 相信所有使用过大模型的开发者都遇到过这些典型痛点:问ChatGPT“今天北京的气温是多少”,它会告诉你“我的知识截止到2023年10月,无法提供实时天气信息”;让它计算“12345.67 * 9…...
DIY焊台实战:用STM32F070F6P6的Encoder模式搞定EC11编码器(附完整CubeMX配置)
DIY焊台实战:用STM32F070F6P6的Encoder模式搞定EC11编码器(附完整CubeMX配置) 在电子DIY的世界里,焊台是每个硬件爱好者的必备工具。而一个精准可控的T12焊台,不仅能提升焊接效率,更能让整个DIY过程充满乐趣…...
告别手动拼报文!用MQTT.fx和OneNet平台快速调试你的ESP8266物联网设备
用MQTT.fx与OneNet构建高效物联网调试工作流 调试物联网设备时,你是否厌倦了反复修改代码、烧录固件、查看串口日志的循环?当ESP8266与OneNet平台通信异常时,传统调试方式往往让我们陷入二进制报文的泥潭。本文将介绍如何通过MQTT.fx这款图形…...
探究MicroBlaze软核在DDR3中运行sleep函数异常延迟的根源与规避策略
1. 现象描述:从BRAM到DDR3的诡异延迟 第一次把MicroBlaze程序从BRAM搬到DDR3运行时,我遇到了一个让人抓狂的问题:原本精准的sleep(1)延时竟然变成了长达数秒的卡顿。这个现象特别容易在Vitis环境下开发网络应用(比如LwIP协议栈&am…...
从信息学奥赛真题到项目实战:C++浮点数精度那些坑,你的double真的够用吗?
从信息学奥赛真题到项目实战:C浮点数精度那些坑,你的double真的够用吗? 在信息学奥赛的赛场上,一个看似简单的多项式计算题可能让许多选手栽跟头——不是算法思路不对,而是浮点数精度处理不当导致答案偏差。这种问题在…...
如何快速掌握MRIcroGL:医学影像三维可视化的完整指南
如何快速掌握MRIcroGL:医学影像三维可视化的完整指南 【免费下载链接】MRIcroGL v1.2 GLSL volume rendering. Able to view NIfTI, DICOM, MGH, MHD, NRRD, AFNI format images. 项目地址: https://gitcode.com/gh_mirrors/mr/MRIcroGL MRIcroGL是一款功能强…...
