20230911 完善了开关屏,释放内存等代码
前段时间在 WPF 中实现了【MediaElement + 定时器】的自动轮播。用了一段时间之后,没啥大问题,但是发现,个别 MP4 视频,在播放的时候,有卡顿的现象,看网上说是 MediaElement 解码的问题,建议使用 VLC,所以,再次改造,用 VLC 代替 MediaElement 来试试。
很多教程,都是使用二次封装 VLC 的库,但我不想使用二次封装的,所以这里直接使用官方的库(以下地址都是官方地址,内容是英文的,暂时没有中文翻译)。
1. 添加引用
<ItemGroup>
<PackageReference Include="LibVLCSharp.WPF" Version="3.7.0" />
<PackageReference Include="VideoLAN.LibVLC.Windows" Version="3.0.18" />
</ItemGroup>
很多教程,都让安装最新的 VLC 播放器,然后去安装目录找 DLL 什么的,其实没必要,官方有相应的库,直接在 Nuget 添加引用就好了(VideoLAN.LibVLC.Windows)。
2. WPF 文件
// 就是添加了 VLC 的命名空间,同时添加了 VLC 的控件(VideoView)
<Window x:Class="WpfVlcDemo.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfVlcDemo"
xmlns:vlc="clr-namespace:LibVLCSharp.WPF;assembly=LibVLCSharp.WPF"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<vlc:VideoView x:Name="Wrapper" />
</Grid>
</Window>
3. CS 文件
using LibVLCSharp.Shared;
using LibVLCSharp.WPF;
using System;
using System.IO;
using System.Linq;
using System.Timers;
using System.Windows;
namespace WpfVlcDemo;
public partial class MainWindow : Window
{
private readonly Timer _timerPlay, _timerON, _timerOFF;
private readonly TimeOnly _timeON, _timeOFF;
private string[] _videos;
private readonly LibVLC _libVlc;
private readonly MediaPlayer _player;
private Media _media, _mediaDefault;
private int _idx = 0;
private bool _isDefault = false;
public MainWindow()
{
InitializeComponent();
// 模拟开/关屏时间
_timeON = TimeOnly.Parse("21:30");
_timeOFF = TimeOnly.Parse("21:35");
#region 初始化计时器
// 开屏计时器
_timerON = new() { Interval = 1000 };
_timerON.Elapsed += TimerON_Elapsed;
// 关屏计时器
_timerOFF = new() { Interval = 1000 };
_timerOFF.Elapsed += TimerOFF_Elapsed;
// 素材播放计时器
_timerPlay = new() { Interval = 10 * 1000 };
_timerPlay.Elapsed += (sender, e) =>
{
(sender as Timer).Stop(); // 停止播放计时
_player.Play(_media);
};
#endregion
#region 初始化并加载播放器
// 初始化播放器
_libVlc = new();
_player = new(_libVlc);
_player.Opening += (sender, e) => _media?.Dispose(); // 释放当前播放素材
_player.Playing += Player_Playing;
// 加载播放器
Wrapper.Loaded += (sender, e) =>
{
(sender as VideoView).MediaPlayer = _player;
_timerON.Start(); // 开启开屏计时器
};
#endregion
#region 获取素材
// 获取全部待播放的素材
Loaded += (sender, e) =>
{
_videos = Directory.GetFiles("<素材路径>");
_mediaDefault = new(_libVlc, "<默认素材路径>");
};
#endregion
}
#region 开屏计时器事件
private void TimerON_Elapsed(object sender, ElapsedEventArgs e)
{
var now = TimeOnly.FromDateTime(DateTime.Now);
if (now < _timeON) // 未到开屏时间
{
if (!_player.IsPlaying) // 播放默认素材
{
_isDefault = true;
_player.Play(_mediaDefault);
}
return;
}
_timerON.Dispose(); // 释放开屏计时器
if (now > _timeOFF) // 超过关屏时间
{
// 播放默认素材
_isDefault = true;
_player.Play(_mediaDefault);
return;
}
_isDefault = false;
_timerOFF.Start(); // 开启关屏计时器
// 开始播放
_media = new(_libVlc, _videos[_idx++]);
_player.Play(_media);
}
#endregion
#region 关屏计时器事件
private void TimerOFF_Elapsed(object sender, ElapsedEventArgs e)
{
var now = TimeOnly.FromDateTime(DateTime.Now);
if (now < _timeOFF)
{
return;
}
_timerOFF.Dispose(); // 释放关屏计时器
#region 方案一:播放默认素材
_isDefault = true;
_player.Play(_mediaDefault);
#endregion
#region 方案二:停止播放
//_timerPlay.Stop(); // 停止播放计时
//// 停止播放,并释放播放器内的素材
//_player.Media.Dispose();
//_player.Stop();
#endregion
}
#endregion
#region 播放器事件
private void Player_Playing(object sender, EventArgs e)
{
if (_isDefault)
{
return;
}
_timerPlay.Start(); // 开启播放计时
if (_idx == _videos.Length)
{
_idx = 0;
}
_media = new(_libVlc, _videos[_idx++]); // 准备下一个素材
}
#endregion
protected override void OnClosed(EventArgs e)
{
_media?.Dispose(); // 释放最后一个播放的素材
_mediaDefault?.Dispose(); // 释放默认素材
_player.Stop();
_player.Dispose();
_libVlc.Dispose();
Wrapper.Dispose();
_timerPlay.Dispose();
base.OnClosed(e);
}
}
至此,代码就全部完毕了,可以直接运行看效果了。
特别说明:20230911 已完善了开关屏、释放内存等相关代码,并适量优化了一下