介绍
在实际的开发过程中,需要在应用内部内嵌播放器进行视频的播放。官方默认的MediaElement控件只能播放有限的视频格式,也不能播放网络流。比较流行的解决方式是vlc的库,但是在实际使用过程中发现有很多问题。这里给大家推荐另一个比较好的库。
使用
官网地址
安装库
Nuget下安装FFME.Windows
PM> Install-Package FFME.Windows
下载ffmpeg依赖
注:官网给的地址我在实际使用中发现,使用官方的代码没问题,使用给定的步骤使用就会抱错,如果你们跟我一样给大家推荐另一个ffmpeg包的地址
ffmpeg依赖下载地址
代码
- 指定ffmpeg库的地址
Unosquare.FFME.Library.FFmpegDirectory = @"C:\ffmpeg\ffmpeg-4.4-windows-desktop-vs2022-gpl-lite\bin";
- xaml中插入控件
<Window x:Class="WpfApp1.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:WpfApp1"
xmlns:ffme="clr-namespace:Unosquare.FFME;assembly=ffme.win"
mc:Ignorable="d"
Loaded="Window_Loaded"
Closed="Window_Closed"
Title="MainWindow" Height="300" Width="600">
<Grid>
<ffme:MediaElement x:Name="Media" Background="Gray" LoadedBehavior="Play" UnloadedBehavior="Manual" />
<Button Content="Play" Click="PlayButton_Click" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" Margin="10"/>
<Button Content="Stop" Click="StopButton_Click" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" Margin="100,10,0,0"/>
</Grid>
</Window>
- 监听失败的事件
Media.MediaFailed += OnMediaFailed;
- 创建一个处理流地址的类
FileInputStream
namespace Unosquare.FFME.Windows.Sample.Foundation;
using Common;
using FFmpeg.AutoGen;
using System;
using System.IO;
using System.Runtime.InteropServices;
/// <inheritdoc />
/// <summary>
/// Provides an example of a very simple custom input stream.
/// </summary>
/// <seealso cref="IMediaInputStream" />
public sealed unsafe class FileInputStream : IMediaInputStream
{
private readonly FileStream BackingStream;
private readonly object ReadLock = new();
private readonly byte[] ReadBuffer;
/// <summary>
/// Initializes a new instance of the <see cref="FileInputStream"/> class.
/// </summary>
/// <param name="path">The path.</param>
public FileInputStream(string path)
{
var fullPath = Path.GetFullPath(path);
BackingStream = File.OpenRead(fullPath);
var uri = new Uri(fullPath);
StreamUri = new Uri(uri.ToString().ReplaceOrdinal("file://", Scheme));
CanSeek = true;
ReadBuffer = new byte[ReadBufferLength];
}
/// <summary>
/// The custom file scheme (URL prefix) including the :// sequence.
/// </summary>
public static string Scheme => "customfile://";
/// <inheritdoc />
public Uri StreamUri { get; }
/// <inheritdoc />
public bool CanSeek { get; }
/// <inheritdoc />
public int ReadBufferLength => 1024 * 16;
/// <inheritdoc />
public InputStreamInitializing OnInitializing { get; }
/// <inheritdoc />
public InputStreamInitialized OnInitialized { get; }
/// <inheritdoc />
public void Dispose()
{
BackingStream?.Dispose();
}
/// <summary>
/// Reads from the underlying stream and writes up to <paramref name="targetBufferLength" /> bytes
/// to the <paramref name="targetBuffer" />. Returns the number of bytes that were written.
/// </summary>
/// <param name="opaque">The opaque.</param>
/// <param name="targetBuffer">The target buffer.</param>
/// <param name="targetBufferLength">Length of the target buffer.</param>
/// <returns>
/// The number of bytes that have been read.
/// </returns>
public int Read(void* opaque, byte* targetBuffer, int targetBufferLength)
{
lock (ReadLock)
{
try
{
var readCount = BackingStream.Read(ReadBuffer, 0, ReadBuffer.Length);
if (readCount > 0)
Marshal.Copy(ReadBuffer, 0, (IntPtr)targetBuffer, readCount);
else if (readCount == 0)
return ffmpeg.AVERROR_EOF;
return readCount;
}
catch (Exception)
{
return ffmpeg.AVERROR_EOF;
}
}
}
/// <inheritdoc />
public long Seek(void* opaque, long offset, int whence)
{
lock (ReadLock)
{
try
{
return whence == ffmpeg.AVSEEK_SIZE ?
BackingStream.Length : BackingStream.Seek(offset, SeekOrigin.Begin);
}
catch
{
return ffmpeg.AVERROR_EOF;
}
}
}
}
- 播放视频
//var target = new Uri(@"rtmp://127.0.0.1/live/test1231233");
var target = new Uri(@"D:\视频\泥坑.mp4");
if (target.ToString().StartsWith(FileInputStream.Scheme, StringComparison.OrdinalIgnoreCase))
await Media.Open(new FileInputStream(target.LocalPath));
else
await Media.Open(target);