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

news2024/11/19 3:21:25

技术背景

在这篇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 callback
            video_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_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_);
            }

	        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.coloradmin.cn/o/1594359.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

群晖双硬盘实时备份(WebDAV Server+Cloud Sync)

安装和设置 WebDAV Server WebDAV 是一种基于HTTP的协议扩展&#xff0c;它允许用户在远程Web服务器上进行文档的编辑和管理&#xff0c;就如同这些文件存储在本地计算机上一样。使用WebDAV&#xff0c;用户可以创建、移动、复制和修改文件和文件夹。 安装和设置 Cloud Sync…

Spring5深入浅出篇:Spring自定义类型转换器

Spring5深入浅出篇:Spring自定义类型转换器 类型转换器 首先要知道什么叫做类型转换器 ,我们通过配置的属性值是以字符串的形式为什么在查看对象成员变量时已经变成了int,这就是Spring的内置类型转换器帮我们做了自动类型转换. 作⽤&#xff1a;Spring通过类型转换器把配置⽂件…

性能分析与限流策略

性能分析与限流策略 常用HTTP服务压测工具介绍 在项目正式上线之前&#xff0c;我们通常需要通过压测来评估当前系统能够支撑的请求量、排查可能存在的隐藏bug&#xff0c;同时了解了程序的实际处理能力能够帮我们更好地匹配项目的实际需求&#xff0c;节约资源成本 压测相关…

服务器主机关机重启告警

提取时间段内系统操作命名&#xff0c;出现系统重启命令&#xff0c;若要出现及时联系确认 重启命令&#xff1a; reboot / init 6 / shutdown -r now&#xff08;现在重启命令&#xff09; 关机命令&#xff1a; init 0 / shutdown -h now&#xff08;关机&#…

C++ 多态实现机制

考虑下面的 C 程序&#xff1a; class A {void func(){} };class B:public A {void func(){} };int main(void) {cout << sizeof(A) << " " << sizeof(B) << endl;return 0; } 输出结果是&#xff1a;1 1 再考虑下面很相似的程序&#xf…

QtCreater 使用

QtCreater 创建项目 1.刚进入 QtCreater 的界面是这样的一个界面 ① 创建一个新的文件&#xff0c;那么我们就选择左上角的 “文件” ② 点击新建文件&#xff0c;或者也可以直接使用快捷键 CtrlN 此时就会弹出对话框&#xff0c;让我们选择想要创建的文件&#xff1a; Appli…

今天掏心窝子!聊聊35岁了程序员何去何从?

今天的内容不聊技术&#xff0c;聊聊轻松的话题&#xff0c;脑子高速转了好几周&#xff0c;停下来思考一下人生…… 不对&#xff0c;关于35岁的问题好像也不轻松&#xff0c;些许有点沉重&#xff0c;反正不是技术&#xff0c;不用高速转动脑细胞了&#xff0c;哈哈。 兄弟…

Spring源码刨析之配置文件的解析和bean的创建以及生命周期

public void test1(){XmlBeanFactory xmlBeanFactory new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));user u xmlBeanFactory.getBean("user",org.xhpcd.user.class);// System.out.println(u.getStu());}先介绍一个类XmlBeanFac…

1027: 舞伴问题

解法&#xff1a; #include<iostream> #include<vector> using namespace std; int main() {int n, m, k;cin >> n;vector<char> man(n);for (int i 0; i < n; i) cin >> man[i];cin >> m;vector<char> woman(m);for (int i 0…

VMware Workstation部署最新版OpenWrt 23.05.3

正文共&#xff1a;1456 字 51 图&#xff0c;预估阅读时间&#xff1a;2 分钟 我们之前介绍了如何在VMware Workstation上安装OpenWrt&#xff08;软路由是啥&#xff1f;OpenWrt又是啥&#xff1f;长啥样&#xff1f;在VMware装一个瞅瞅&#xff09;&#xff0c;也介绍了如何…

C++类和对象中下篇

赋值运算符重载 运算符重载 C为了增强代码的可读性引入了运算符重载&#xff0c;运算符重载是具有特殊函数名的函数&#xff0c;也具有其返回值类型&#xff0c;函数名字以及参数列表&#xff0c;其返回值类型与参数列表与普通的函数类似。 函数名字为&#xff1a;关键字ope…

【数学】主成分分析(PCA)的详细深度推导过程

本文基于Deep Learning (2017, MIT)&#xff0c;推导过程补全了所涉及的知识及书中推导过程中跳跃和省略的部分。 blog 1 概述 现代数据集&#xff0c;如网络索引、高分辨率图像、气象学、实验测量等&#xff0c;通常包含高维特征&#xff0c;高纬度的数据可能不清晰、冗余&am…

QQ农场-phpYeFarm添加数据教程

前置知识 plugin\qqfarm\core\data D:\study-project\testweb\upload\source\plugin\qqfarm\core\data 也就是plugin\qqfarm\core\data是一个缓存文件,如果更新农场数据后,必须要删除才可以 解决种子限制(必须要做才可以添加成功) 你不更改加入了id大于2000直接删除种子 D…

Bridge 桥接

意图 将抽象部分与其显示部分分离&#xff0c;使他们都可以独立地变化。 结构 其中&#xff1a; Abstraction定义抽象类的接口&#xff0c;维护一个指向Implementer类型对象的指针。RefinedAbstraction扩展由Abstraction定义的接口。Implementor定义实现类的接口&#xff0c…

2024年DTC的回顾与思考

刚结束了2024的数据库技术嘉年华 这是我从2017年开始就参加的技术大会。中途因为疫情的耽误。正常来说我是连续的。知道我的朋友都知道我习惯炫耀一下。 按照惯例&#xff0c;此时此刻群友都在写大会回顾。只是有几个不讲武德的人已经发送了。下面有主观和客观的分析。 主观上…

亚马逊云科技CTO带你学习云计算降本增效秘诀

2023亚马逊云科技一年一度的重磅春晚--Re:invent上有诸多不同话题的主题Keynote&#xff0c;这次小李哥带大家复盘来自亚马逊CTO: Wener博士的主题演讲: 云架构节俭之道1️⃣节俭对于云计算为什么重要&#xff1f; ▶️企业基础设施投入大&#xff0c;利用好降本策略可以减少巨…

记录linux从0部署java项目(宝塔)

目录 一、安装宝塔可视化界面 二、部署前端 三、部署后端 1、配置并连接Mysql数据库 2、配置并连接redis 3、安装jdk 这里先记录一个安装后遇到的问题 安装openJDK 四、检查 一、安装宝塔可视化界面 宝塔面板下载&#xff0c;免费全能的服务器运维软件 运行安装脚本 安…

步骤大全:网站建设3个基本流程详解

一.领取一个免费域名和SSL证书&#xff0c;和CDN 1.打开网站链接&#xff1a;https://www.rainyun.com/z22_ 2.在网站主页上&#xff0c;您会看到一个"登陆/注册"的选项。 3.点击"登陆/注册"&#xff0c;然后选择"微信登录"选项。 4.使用您的…

时间序列模型:lag-Llama

项目地址&#xff1a;GitHub - time-series-foundation-models/lag-llama: Lag-Llama: Towards Foundation Models for Probabilistic Time Series Forecasting 论文地址&#xff1a;https://arxiv.org/pdf/2310.08278.pdf hugging-face镜像&#xff1a;https://hf-mirror.c…

C++11 设计模式2. 简单工厂模式

简单工厂&#xff08;Simple Factory&#xff09;模式 我们从实际例子出发&#xff0c;来看在什么情况下&#xff0c;应用简单工厂模式。 还是以一个游戏举例 //策划&#xff1a;亡灵类怪物&#xff0c;元素类怪物&#xff0c;机械类怪物&#xff1a;都有生命值&#xff0…