WPF下播放Rtmp的解决方案

介绍

在实际的开发过程中,需要在应用内部内嵌播放器进行视频的播放。官方默认的MediaElement控件只能播放有限的视频格式,也不能播放网络流。比较流行的解决方式是vlc的库,但是在实际使用过程中发现有很多问题。这里给大家推荐另一个比较好的库。

使用

官网地址

安装库

Nuget下安装FFME.Windows

bash 复制代码
PM> Install-Package FFME.Windows

下载ffmpeg依赖

注:官网给的地址我在实际使用中发现,使用官方的代码没问题,使用给定的步骤使用就会抱错,如果你们跟我一样给大家推荐另一个ffmpeg包的地址
ffmpeg依赖下载地址

代码

  1. 指定ffmpeg库的地址
csharp 复制代码
Unosquare.FFME.Library.FFmpegDirectory = @"C:\ffmpeg\ffmpeg-4.4-windows-desktop-vs2022-gpl-lite\bin";
  1. xaml中插入控件
csharp 复制代码
<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>
  1. 监听失败的事件
csharp 复制代码
Media.MediaFailed += OnMediaFailed;
  1. 创建一个处理流地址的类FileInputStream
csharp 复制代码
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;
            }
        }
    }
}
  1. 播放视频
csharp 复制代码
//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);
相关推荐
晚安苏州8 小时前
WPF DataTemplate 数据模板
wpf
甜甜不吃芥末1 天前
WPF依赖属性详解
wpf
Hat_man_1 天前
WPF制作图片闪烁的自定义控件
wpf
晚安苏州3 天前
WPF Binding 绑定
wpf·wpf binding·wpf 绑定
wangnaisheng3 天前
【WPF】RenderTargetBitmap的使用
wpf
dotent·3 天前
WPF 完美解决改变指示灯的颜色
wpf
orangapple5 天前
WPF 用Vlc.DotNet.Wpf实现视频播放、停止、暂停功能
wpf·音视频
ysdysyn5 天前
wpf mvvm 数据绑定数据(按钮文字表头都可以),根据长度进行换行,并把换行的文字居中
c#·wpf·mvvm
orangapple5 天前
WPF 使用LibVLCSharp.WPF实现视频播放、停止、暂停功能
wpf
晚安苏州5 天前
WPF ControlTemplate 控件模板
wpf