• 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文件地址;

    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
        {
            /// 
            /// Interaction logic for App.xaml
            /// 
            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
        {
            /// 
            /// Interaction logic for MainWindow.xaml
            /// 
            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

     

  • 相关阅读:
    3-知识补充-MVC框架
    C语言K&R圣经笔记 2.4声明 2.5算术操作符 2.6关系和逻辑操作符
    springboot采用协同过滤算法的家政服务平台的设计与实现毕业设计源码260839
    竞赛选题 大数据疫情分析及可视化系统
    GET 请求和 POST 请求的区别和使用
    django的orm框架基础使用
    量表如何分析?
    网页版网络聊天室设计与实现(Java+SSH+MySQL)
    【实践篇】推荐算法PaaS化探索与实践
    CVE-2020-1957 Apache Shiro 认证绕过漏洞
  • 原文地址:https://www.cnblogs.com/maoleigepu/p/17727894.html