WPF中以MVVM方式,实现RTSP视频播放

前言

视频播放在上位机开发中经常会遇到,基本上是两种常见的解决方案

1.采用厂家提供的sdk和前端控件进行展示,常见的海康/大华都提供了相关sdk及文档

2.开启相机onvif协议,捅过rtsp视频流进行播放,前端可以采用web方式,或者wpf中的视频控件进行展示。

项目需求,决定了最终采用开启相机onvif供能,wpf中播放的方式。

网络调研一阵子之后,基本都是推荐Vlc.DotNet或者libvlcsharp.wpf进行前端展示。

参考了很多代码,无论是官方文档,还是不同博客里的代码,很难做到用mvvm的方式对于逻辑解耦。

而且Vlc.DotNet已经不再更新了。

Libvlcasharp.wpf的设计有些反人类,可以参考这篇文章WPF中使用LibVLCSharp.WPF 播放rtsp - Naylor - 博客园 (cnblogs.com)

所以这部分逻辑写的很难受,需要寻找其他方案。

最近有空了,调研了几个其他开源项目,大家的思路都比较一致,相机打开onvif协议推送rtsp视频流,本地通过ffmpeg进行视频转流,然后推送到wpf前端控件上。

unosquare/ffmediaelement: FFME: The Advanced WPF MediaElement (based on FFmpeg) (github.com)

SuRGeoNix/Flyleaf: Media Player .NET Library for WinUI 3/ WPF/WinForms (based on FFmpeg/DirectX) (github.com)

网上有FFME的样例代码,我在本地搭建没有成功,应该是我的ffmpeg编译版本问题,可以参考这个项目。

DG-Wangtao/FFMEVideoPlayer: 使用FFmepg封装的WPF MideaElement,可以播放rtsp视频流。感谢 https://github.com/unosquare/ffmediaelement

最终选择了Flyleaf的方案,简单搭建了demo给大家参考。

Flyleaf官方项目地址SuRGeoNix/Flyleaf: Media Player .NET Library for WinUI 3/ WPF/WinForms (based on FFmpeg/DirectX) (github.com)

MVVM框架使用的是CommunityToolKit.MVVM

正文

Flyleaf的使用整体分成四步走,

1.App.xaml及App.xaml.cs中配置ffmpeg的dll文件地址;

2.ViewModel中配置参数等信息;

3.View中配置布局等信息;

4.在xaml.cs中确定View和ViewModel的绑定关系

  • App.xaml.cs中配置ffmpege的地址

 1.1ffmpeg的dll文件,我才用的是Flyleaf官方sample中的文件,版本不是最新的。

1.2文件统一放在项目中的FFmpeg文件夹中

1.3生成操作(Build Action)配置为 无(None)

1.4复制到输出目录(Copy to Output Directory)配置为 如果较新则复制(Copy if newer)

1.5App.xaml中添加startup事件

	<Application x:Class="FlyleafDemo.App"
	             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	             xmlns:local="clr-namespace:FlyleafDemo"
	             StartupUri="MainWindow.xaml"
	             Startup="Application_Startup">
	    <Application.Resources>
	         
	    </Application.Resources>
	</Application>

1.6App.xaml.cs中配置ffmpeg的dll路径,项目编译后会复制ffmpeg文件夹及dll。

	using FlyleafLib;
	using System;
	using System.Collections.Generic;
	using System.Configuration;
	using System.Data;
	using System.Linq;
	using System.Threading.Tasks;
	using System.Windows;
	
	namespace FlyleafDemo
	{
	    /// <summary>
	    /// Interaction logic for App.xaml
	    /// </summary>
	    public partial class App : Application
	    {
	        private void Application_Startup(object sender, StartupEventArgs e)
	        {
	            Engine.Start(new EngineConfig()
	            {
	                FFmpegPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "FFmpeg"),
	                FFmpegDevices = false,    // Prevents loading avdevice/avfilter dll files. Enable it only if you plan to use dshow/gdigrab etc.
	
	#if RELEASE
	                FFmpegLogLevel      = FFmpegLogLevel.Quiet,
	                LogLevel            = LogLevel.Quiet,
	
	#else
	                FFmpegLogLevel = FFmpegLogLevel.Warning,
	                LogLevel = LogLevel.Debug,
	                LogOutput = ":debug",
	                //LogOutput         = ":console",
	                //LogOutput         = @"C:\Flyleaf\Logs\flyleaf.log",                
	#endif
	
	                //PluginsPath       = @"C:\Flyleaf\Plugins",
	
	                UIRefresh = false,    // Required for Activity, BufferedDuration, Stats in combination with Config.Player.Stats = true
	                UIRefreshInterval = 250,      // How often (in ms) to notify the UI
	                UICurTimePerSecond = true,     // Whether to notify UI for CurTime only when it's second changed or by UIRefreshInterval
	            });
	        }
	    }
	}
  • ViewModel中配置参数,简单来说就是有个button,点击切换rtsp的url,前端控件进行播放
	using CommunityToolkit.Mvvm.ComponentModel;
	using CommunityToolkit.Mvvm.Input;
	using FlyleafLib.MediaPlayer;
	using FlyleafLib;
	using System;
	using System.Collections.Generic;
	using System.Linq;
	using System.Text;
	using System.Threading.Tasks;
	using System.Windows.Media;
	
	namespace FlyleafDemo
	{
	    public class MainViewModel:ObservableObject
	    {
	        private Player player;
	
	        public Player Player
	        {
	            get => player;
	            set => SetProperty(ref player, value);
	        }
	
	        private Config config;
	
	        public Config Config
	        {
	            get => config;
	            set => SetProperty(ref config, value);
	        }
	
	        private string uriString;
	
	        public string UriString
	        {
	            get => uriString;
	            set => SetProperty(ref uriString, value);
	        }
	
	        public IRelayCommand<string> PlayCommand { get; set; }
	        public MainViewModel()
	        {
	            Config = new Config();
	            Config.Video.BackgroundColor = Colors.Transparent;
	            // 设置播放延迟为100ms,可能我理解有误,具体可以在项目issues里查看
	            // Config.Player.MaxLatency = 100 * 10000;
	
	            Player = new Player(Config);
	            PlayCommand = new RelayCommand<string>(PlayAction);
	            UriString = uri1;
	        }
	
	        private string currentUri = string.Empty;
	        private string uri1 = "rtsp://192.168.20.2:554/cam/realmonitor?channel=1&subtype=0&unicast=true&proto=Onvif";
	        private string uri2 = "rtsp://192.168.20.3:554/cam/realmonitor?channel=1&subtype=0&unicast=true&proto=Onvif";
	        private void PlayAction(string uri)
	        {
	            if (!string.IsNullOrEmpty(uri))
	            {
	                if (currentUri == uri1)
	                {
			//Player.Commands.Stop.Execute(null);
	                    currentUri = uri2;
	                    Player.Commands.Open.Execute(uri2);
	                }
	                else
	                {
			//Player.Commands.Stop.Execute(null);
	                    currentUri = uri1;
	                    Player.Commands.Open.Execute(uri1);
	                }
	            }
	        }
	    }
	}
  • View中配置布局
	<Window
	    x:Class="FlyleafDemo.MainWindow"
	    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
	    xmlns:fl="clr-namespace:FlyleafLib.Controls.WPF;assembly=FlyleafLib"
	    xmlns:local="clr-namespace:FlyleafDemo"
	    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
	    Title="MainWindow"
	    Width="800"
	    Height="450"
	    mc:Ignorable="d">
	    <Grid>
	        <Grid.RowDefinitions>
	            <RowDefinition Height="5*" />
	            <RowDefinition Height="*" />
	        </Grid.RowDefinitions>
	        <fl:FlyleafHost
	            AttachedDragMove="Both"
	            KeyBindings="Both"
	            Player="{Binding Player, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
	            <Viewbox>
	                <TextBlock Foreground="DarkOrange" Text="Hello Flyleaf Overlay!" />
	            </Viewbox>
	        </fl:FlyleafHost>
	        <Button
	            Grid.Row="1"
	            Command="{Binding PlayCommand}"
	            CommandParameter="{Binding UriString, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
	    </Grid>
	</Window>
  • 在xaml.cs中确定View和ViewModel的绑定关系
	using System;
	using System.Collections.Generic;
	using System.Linq;
	using System.Text;
	using System.Threading.Tasks;
	using System.Windows;
	using System.Windows.Controls;
	using System.Windows.Data;
	using System.Windows.Documents;
	using System.Windows.Input;
	using System.Windows.Media;
	using System.Windows.Media.Imaging;
	using System.Windows.Navigation;
	using System.Windows.Shapes;
	
	namespace FlyleafDemo
	{
	    /// <summary>
	    /// Interaction logic for MainWindow.xaml
	    /// </summary>
	    public partial class MainWindow : Window
	    {
	        public MainWindow()
	        {
	            InitializeComponent();
	            this.DataContext = new MainViewModel();
	        }
	    }
	}

总结 

总结

前端控件绑定比较方便,减少了在xaml.cs中的耦合逻辑

我尝试过三路视频同时播放,效果不错,系统资源消耗也不高

很多参数都可以在Config中配置,一些交互逻辑可以在Player中执行,比较清晰

但是,单视频控件切换视频流的时候,会有一定时间延迟,我尝试过使用

Player.Commands.Stop.Execute(null);

但效果不大。

感兴趣的可以深挖源码,我这里只是抛砖引玉。

Demo代码地址https://gitee.com/maoleigepu/flyleaf-demo.git ,或者去github,maoleigepu/FlyleafDemo (github.com)效果图如下

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
MVVM模式下实现WPF的鼠标移动Canvas的Ellipse控件,可以使用以下步骤: 1.在ViewModel定义Ellipse控件的位置属性,例如: ``` private double _ellipseLeft; public double EllipseLeft { get { return _ellipseLeft; } set { _ellipseLeft = value; OnPropertyChanged(nameof(EllipseLeft)); } } private double _ellipseTop; public double EllipseTop { get { return _ellipseTop; } set { _ellipseTop = value; OnPropertyChanged(nameof(EllipseTop)); } } ``` 2.在XAML绑定Ellipse控件的位置属性到ViewModel定义的属性。例如: ``` <Canvas> <Ellipse Width="50" Height="50" Fill="Red" Canvas.Left="{Binding EllipseLeft}" Canvas.Top="{Binding EllipseTop}" MouseMove="Ellipse_MouseMove"/> </Canvas> ``` 3.在ViewModel实现Ellipse_MouseMove事件处理程序。例如: ``` private void Ellipse_MouseMove(object sender, MouseEventArgs e) { if (e.LeftButton == MouseButtonState.Pressed) { EllipseLeft = e.GetPosition(canvas).X - 25; EllipseTop = e.GetPosition(canvas).Y - 25; } } ``` 在这个事件处理程序,我们首先检查鼠标左键是否按下。如果是,我们就更新ViewModel的EllipseLeft和EllipseTop属性。这将自动更新绑定的Ellipse控件的位置。 注意,要使用MVVM模式实现WPF的鼠标移动Canvas的Ellipse控件,需要使用Command模式来实现MouseDown、MouseMove和MouseUp事件处理程序。这是因为,MVVM模式要求将所有用户交互操作转移到ViewModel处理,而不是直接在View处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值