前言
视频播放在上位机开发中经常会遇到,基本上是两种常见的解决方案
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)
网上有FFME的样例代码,我在本地搭建没有成功,应该是我的ffmpeg编译版本问题,可以参考这个项目。
最终选择了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文件地址;
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 }); } } }
2.ViewModel中配置参数等信息;
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); } } } } }
3.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>
4.在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)效果图如下
————————————————
版权声明:本文为CSDN博主「maoleigepu」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/maoleigepu/article/details/133268837