.Net8 Avalonia跨平台UI框架——<vlc:VideoView>控件播放海康监控、摄像机视频(Windows / Linux)

ops/2025/1/15 14:00:48/

一、UI效果

在这里插入图片描述
在这里插入图片描述

二、新建用户控件:VideoViewControl.axaml

需引用:VideoLAN.LibVLC.Windows包
Linux平台需安装:VLC 和 LibVLC (sudo apt-get update、sudo apt-get install vlc libvlccore-dev libvlc-dev)

.axaml 代码

注:vlc:VideoView 上无法增加鼠标和指针事件,需使用Popup浮动透明层

<UserControl xmlns="https://github.com/avaloniaui"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:vlc="clr-namespace:LibVLCSharp.Avalonia;assembly=LibVLCSharp.Avalonia"xmlns:vm="using:TrainArrivalAnalysis.Avalonia.ViewModels"xmlns:i="clr-namespace:Avalonia.Xaml.Interactivity;assembly=Avalonia.Xaml.Interactivity"xmlns:controls="clr-namespace:Avalonia.Controls;assembly=Avalonia.Controls"mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"x:Class="TrainArrivalAnalysis.Avalonia.Controls.VideoViewControl"><Design.DataContext><vm:VideoWindowViewModel/></Design.DataContext><Grid><vlc:VideoView x:Name="playerView" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"  IsHitTestVisible="False" SizeChanged="playerViewSizeChanged" ></vlc:VideoView><Popup x:Name="videoViewPopup" Placement="Center" PlacementTarget="{Binding ElementName=playerView}" IsOpen="True" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"><Border Background="Transparent" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ><Grid Background="Transparent" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"><TextBlock x:Name="playBackTip" VerticalAlignment="Top" HorizontalAlignment="Right" Background="#F0F8FF" Foreground="Red" Margin="0,50,10,0" Padding="15" FontSize="16" FontWeight="Bold" IsVisible="False" /><TextBlock x:Name="connectTip" VerticalAlignment="Center" HorizontalAlignment="Center" Background="Transparent" Foreground="White" FontSize="40" Text="网络中断" IsVisible="False" /></Grid></Border></Popup></Grid></UserControl>
.axaml.cs 代码
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Extensions.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using Avalonia.Threading;
using Avalonia.VisualTree;
using LibVLCSharp.Avalonia;
using LibVLCSharp.Shared;
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Net.NetworkInformation;
using System.Threading;
using System.Threading.Tasks;
using TrainArrivalAnalysis.Utility;namespace TrainArrivalAnalysis.Avalonia.Controls;public partial class VideoViewControl : UserControl, INotifyPropertyChanged, IDisposable
{private bool _disposed = false;private LibVLC _libVLC;private MediaPlayer _mediaPlayer;private string _url;public string Url{get => _url;set{if (_url != value){StopPlayback(); // 停止当前播放_url = value;OnPropertyChanged(nameof(Url));if (!string.IsNullOrEmpty(_url)){SetMediaPlayerAsync();}}}}private int _type = 0;public int Type{get => _type;set{_type = value;OnPropertyChanged(nameof(Type));}}private bool _isPlayBackTip = false;public bool IsPlayBackTip{get => _isPlayBackTip;set{_isPlayBackTip = value;OnPropertyChanged(nameof(IsPlayBackTip));Dispatcher.UIThread.InvokeAsync(() =>{playBackTip.IsVisible = _isPlayBackTip;});}}private string _playBackTipContent;public string PlayBackTipContent{get => _playBackTipContent;set{if (_playBackTipContent != value){_playBackTipContent = value;OnPropertyChanged(nameof(PlayBackTipContent));Dispatcher.UIThread.InvokeAsync(() =>{playBackTip.Text = _playBackTipContent;});}}}private bool _isConnectTip = false;public bool IsConnectTip{get => _isConnectTip;set{_isConnectTip = value;OnPropertyChanged(nameof(IsConnectTip));Dispatcher.UIThread.InvokeAsync(() =>{connectTip.IsVisible = _isConnectTip;});}}public event PropertyChangedEventHandler PropertyChanged;protected virtual void OnPropertyChanged(string propertyName){PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));}private int _maxRetryAttempts = 5;private int _retryDelayMilliseconds = 5000;private int _currentRetryAttempt = 0;public VideoViewControl(){InitializeComponent();var libVlcOptions = new[] { "--no-video-title-show", "--no-osd" };_libVLC = new LibVLC(libVlcOptions);this.Loaded += async (sender, e) =>{var videoView = this.FindControl<VideoView>("playerView");if (videoView != null){videoView.Loaded += async (s, ev) =>{if (!string.IsNullOrEmpty(Url)){await SetMediaPlayerAsync();}};}};this.Unloaded += (sender, e) =>{StopPlayback();Dispose(false);};}public async Task SetMediaPlayerAsync(){try{if (!string.IsNullOrEmpty(Url)){_mediaPlayer = new MediaPlayer(_libVLC);await Dispatcher.UIThread.InvokeAsync(() =>{VideoView _videoView = this.FindControl<VideoView>("playerView");if (_videoView != null){_videoView.MediaPlayer = _mediaPlayer;}});var mediaOptions = new[] { ":network-caching=300", "avcodec-hw=any" };var _media = new Media(_libVLC, Url, FromType.FromLocation, mediaOptions);_mediaPlayer.Media = _media;_mediaPlayer.EncounteredError += OnEncounteredError;await Task.Run(() =>{_mediaPlayer.Play();});}_currentRetryAttempt = 0;_ = CheckPlayStatus().ConfigureAwait(false);}catch (Exception ex){}}private async Task CheckPlayStatus(){try{await Task.Delay(5000).ConfigureAwait(false);while (true){if (_mediaPlayer != null && _mediaPlayer.IsPlaying){IsConnectTip = false;if (Type == 1){IsPlayBackTip = true;}_currentRetryAttempt = 0;}else if (_mediaPlayer.State == VLCState.Stopped || _mediaPlayer.State == VLCState.Error || (_mediaPlayer.State == VLCState.Ended && !NetworkHelper.IsNetworkConnected())){IsPlayBackTip = false;IsConnectTip = true;StopPlayback();}else if (_mediaPlayer.State == VLCState.Ended){IsConnectTip = false;if (Type == 1){IsPlayBackTip = true;}}await Task.Delay(10000).ConfigureAwait(false);}}catch (OperationCanceledException){}catch (Exception ex){}}private async void OnEncounteredError(object sender, EventArgs e){IsPlayBackTip = false;IsConnectTip = true;}private async void playerViewSizeChanged(object sender, SizeChangedEventArgs e){if (videoViewPopup.IsOpen){videoViewPopup.Width = playerView.ActualWidth();videoViewPopup.Height = playerView.ActualHeight();}}// 停止播放private void StopPlayback(){_mediaPlayer?.Stop();}public void Dispose(){Dispose(true);GC.SuppressFinalize(this);}protected virtual void Dispose(bool disposing){if (!_disposed){if (disposing){if (_mediaPlayer != null){_mediaPlayer.Stop();_mediaPlayer.Dispose();_mediaPlayer = null;}if (_libVLC != null){_libVLC.Dispose();_libVLC = null;}}_disposed = true;}}
}

三、主页面MainWindow.axaml使用

.axaml代码
<Window xmlns="https://github.com/avaloniaui"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:animation="clr-namespace:Avalonia.Animation;assembly=Avalonia.Animation"xmlns:vm="using:TrainArrivalAnalysis.Avalonia.ViewModels"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:controls="clr-namespace:TrainArrivalAnalysis.Avalonia.Controls"xmlns:converters="clr-namespace:TrainArrivalAnalysis.Avalonia.Converters"xmlns:md="clr-namespace:TrainArrivalAnalysis.Avalonia.Models.Dto"mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"x:Class="TrainArrivalAnalysis.Avalonia.Views.MainWindow"x:DataType="md:ArrivalRecordDto"Icon="/Assets/avalonia-logo.ico"Title="半自动闭塞区段列车整列到达自动智能分析系统"WindowState="FullScreen" SystemDecorations="None"  ZIndex="1"><Window.Resources><converters:GridLengthToDoubleConverter x:Key="GridLengthToDoubleConverter"/></Window.Resources><Window.Styles><Style Selector="Button.blueBtn"><Setter Property="Foreground" Value="White"/><Setter Property="Background" Value="#4C7DF7"/><Setter Property="Height" Value="32"/><Setter Property="FontSize" Value="16"/><Setter Property="BorderBrush" Value="DarkGray"/><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="Button"><Border x:Name="containerBorder" BorderBrush="{TemplateBinding BorderBrush}" Background="#4C7DF7" CornerRadius="13"><ContentPresenter Content="{TemplateBinding Content}" HorizontalAlignment="Center" VerticalAlignment="Center" ></ContentPresenter></Border></ControlTemplate></Setter.Value></Setter></Style></Window.Styles><Grid  x:Name="RootGrid" RowDefinitions="Auto,*"  SizeChanged="RootGridSizeChanged"><Grid x:Name="TopGird"  Grid.Row="0" Height="2" Background="Black" PointerMoved="TopGridPointerMovedHandler"> </Grid><Grid Grid.Row="1"  Background="Black"><Grid.ColumnDefinitions><ColumnDefinition Width="*" /><ColumnDefinition Width="*" /></Grid.ColumnDefinitions><Grid.RowDefinitions><RowDefinition Height="*" /><RowDefinition Height="*" /></Grid.RowDefinitions><controls:VideoViewControl  Grid.Row="0" Grid.Column="0" x:Name="videoView"  DoubleTapped="VideoDoubleTappedHandler" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/><controls:VideoViewControl  Grid.Row="0" Grid.Column="1" x:Name="videoView1" DoubleTapped="VideoDoubleTappedHandler" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/><Image x:Name="imageArriva" Grid.Row="1" Grid.Column="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"   Stretch="Fill" DoubleTapped="ImageDoubleTappedHandler"></Image><Image x:Name="imageLeave" Grid.Row="1" Grid.Column="1"  HorizontalAlignment="Stretch" VerticalAlignment="Stretch"   Stretch="Fill" DoubleTapped="ImageDoubleTappedHandler" ></Image><Image x:Name="imageArriva_1" Grid.Row="0" Grid.Column="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"   Stretch="Fill" DoubleTapped="ImageDoubleTappedHandler"></Image><Image x:Name="imageLeave_1" Grid.Row="0"  Grid.Column="1"  HorizontalAlignment="Stretch" VerticalAlignment="Stretch"   Stretch="Fill" DoubleTapped="ImageDoubleTappedHandler" ></Image></Grid><Popup x:Name="NavMenuPopup" Placement="Top" PlacementTarget="{Binding ElementName=TopGird}" IsOpen="False" IsVisible="False"><Border x:Name="PopupBorder"  Background="#F0F8FF"  HorizontalAlignment="Stretch" VerticalAlignment="Stretch"><Grid x:Name="NavMenuGird" Background="Transparent" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" ColumnDefinitions="Auto,*,Auto" MinHeight="30" PointerExited="NavMenuPointerExitedHandler"><StackPanel Orientation="Horizontal" Grid.Column="0" HorizontalAlignment="Left"><Button Content="标记为来车信号" Margin="5" Classes="blueBtn" Width="125" x:Name="arrivalSignalButton" Click="ArrivalSignal_Click" DockPanel.Dock="Left" /><TextBox x:Name="searchStartTime" Watermark="取图开始日期" Text="" Height="32" /><TextBox x:Name="searchEndTime" Watermark="取图结束日期" Text="" Height="32" /><Button Content="标记为无车信号" Margin="5" Classes="blueBtn" Width="125" x:Name="leaveSignalButton" Click="LeaveSignal_Click" DockPanel.Dock="Left" /></StackPanel><Rectangle Grid.Column="1" Fill="Transparent"/><StackPanel Orientation="Horizontal" Grid.Column="2" HorizontalAlignment="Right"><Button Content="最近来车记录" Margin="5" Classes="blueBtn" Width="125" x:Name="showLastArrivalRecordButton" Click="ShowLastArrivalRecordClick" /><Button Content="来车记录查询" x:Name="showArrivalRecordButton" Margin="5" Classes="blueBtn" Width="125" Click="ShowArrivalRecordClick" /><Button Content="结束查询" x:Name="closeArrivalRecordButton" Margin="5" Classes="blueBtn" Width="125" Click="CloseArrivalRecordClick"/><Button Content="测试下载" x:Name="downLoadButton" Margin="5" Classes="blueBtn" Width="125" Click="DownLoadClick" IsVisible="False" /><Button Content="关闭系统" Margin="50,5,5,5" Classes="blueBtn" Width="125" Click="CloseAppClick" /></StackPanel></Grid></Border></Popup><Popup x:Name="ArrivalRecordPopup" Placement="Center" PlacementTarget="{Binding ElementName=RootGrid}" IsOpen="False" ><Border x:Name="ArrivalRecordPopupBorder"  Background="White"  HorizontalAlignment="Stretch" VerticalAlignment="Stretch"><controls:ArrivalRecordViewControl x:Name="arrivalRecordView"  HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/></Border></Popup></Grid>
</Window>
.axaml.cs代码
 public partial class MainWindow : Window{public MainWindow()
{InitializeComponent();this.Loaded += async (sender, e) =>{string url = $"rtsp://用户名:密码@IP/LiveMedia/ch1/Media1";SetVideoViewUrl("videoView", url, "");url = $"rtsp://用户名:密码@IP/LiveMedia/ch1/Media1";SetVideoViewUrl("videoView1", url, "");};}public async void SetVideoViewUrl(string videoViewControlName, string url, string playBackTipContent, int type = 0, bool isPlayBackTip = false)
{_ = Task.Run(async () =>{await Dispatcher.UIThread.InvokeAsync(() =>{var _videoView = this.FindControl<VideoViewControl>(videoViewControlName);if (_videoView != null){_videoView.Url = url;_videoView.Type = type;_videoView.PlayBackTipContent = playBackTipContent;_videoView.IsPlayBackTip = isPlayBackTip;}});});
}
}

http://www.ppmy.cn/ops/150299.html

相关文章

【硬件介绍】Type-C接口详解

一、Type-C接口概述 Type-C接口特点&#xff1a;以其独特的扁头设计和无需区分正反两面的便捷性而广受欢迎。这种设计大大提高了用户的使用体验&#xff0c;避免了传统USB接口需要多次尝试才能正确插入的问题。Type-C接口内部结构&#xff1a;内部上下两排引脚的设计虽然可能不…

pwn-堆利用入门例题

堆入门好难啊,但十分具有挑战性 题目链接:PolarD&N 1.heap_Easy_Uaf:(Use after free) 漏洞函数在这里,向a中写入flag后free了a,但指针仍然指向a. 那么此时fast-bins中就有一个大小为0x78的堆块,a指向这个堆块,如果我们申请一个大小为0x68的堆块的话(b),a与b就都指向这个…

ubuntu各分区的用途

在 Ubuntu 中&#xff0c;分区是将硬盘划分为多个逻辑部分的过程&#xff0c;每个分区可以用于不同的用途。合理分区可以提高系统性能、数据安全性和管理效率。以下是 Ubuntu 中常见分区及其用途的详细说明&#xff1a; 1. 根分区 (/) 用途&#xff1a;存放操作系统核心文件、…

性能工具之 JMeter ActiveMQ 脚本开发实践

文章目录 一、ActiveMQ环境搭建1.1、控制台环境搭建1.2、控制台配置 二、SpringBoot开发环境搭建三、JMeter脚本二次开发环境搭建 一、ActiveMQ环境搭建 1.1、控制台环境搭建 下载 ActiveMQ 地址为&#xff1a; https://activemq.apache.org/components/classic/download/ 注…

yum和vim的使用

目录 一.Linux软件包管理器yum 二.Linux编辑器vim使用 1.vim的基本概念 2.vim的基本操作 3.vim正常模式命令 4.vim末行模式命令 5.vim的配置 一.Linux软件包管理器yum 什么是软件包 在Linux下安装软件, 一个通常的办法是下载到程序的源代码, 并进行编译, 得到可执行程序…

反转字符串力扣--344

目录 题目 思路 双指针法 代码 双指针法 题目 编写一个函数&#xff0c;其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。 不要给另外的数组分配额外的空间&#xff0c;你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。 示例 1&#…

数字普惠金融对新质生产力的影响研究(2015-2023年)

基于2015—2023年中国制造业上市公司数据&#xff0c;探讨了数字普惠金融对制造业企业新质生产力的影响及作用机理。研究发现&#xff0c;数字普惠金融有助于促进制造业企业新质生产力的发展&#xff0c;尤其是在数字普惠金融的使用深度较大的情况下&#xff0c;其对新质生产力…

Windows图形界面(GUI)-QT-C/C++ - QT信号与槽机制详解

公开视频 -> 链接点击跳转公开课程博客首页 -> ​​​链接点击跳转博客主页 目录 Qt消息机制概述 什么是信号与槽 基本概念 信号(Signals)详解 信号的特征 信号的定义方式 槽(Slots)详解 槽的特征 槽的定义方式 信号与槽的连接 基本连接语法 连接类型 高级特…