前言
WPF开发中,实时播放RTSP视频流是一个常见的需求,尤其是在监控系统和多媒体应用中。然而,实现这一功能并不简单,需要解决网络通信、视频解码以及与WPF界面的无缝集成等问题。
本文将介绍几种在WPF中播放RTSP视频流的方法,帮助大家快速实现这视频流播放功能。
正文
在WPF中可以使用LibVLCSharp.WPF、Vlc.DotNet.Wpf组件直接播放RTSP流,此外还可以通过LibVLCSharp + SkiaSharp的方式将VLC视频流通过WriteableBitmap的方式关联到Image对象。
1、LibVLCSharp.WPF
使用VideoView控件可以方便地播放RTSP视频流。该方法使用简单,但是不能很好的对RTSP流图像进行处理。
引入NuGet包
LibVLCSharp.WPF。
初始化LibVLCSharp
LibVLCSharp.Shared.Core.Initialize();
使用VideoView控件
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<wpf:VideoView x:Name="VideoView"/>
</Grid>
初始化LibVLC对象
_libVlc = new LibVLC(true);
_mediaPlayer = new MediaPlayer(_libVlc)
{
Volume = 0, //静音
EnableHardwareDecoding = true //硬件加速
};
_mediaPlayer.EndReached += OnPlayerEndReached;
_mediaPlayer.Stopped += OnPlayerStopped;
VideoView.IsManipulationEnabled = true;
VideoView.IsEnabled = true;
VideoView.Loaded += (sender, args) =>
{
VideoView.MediaPlayer = _mediaPlayer;
};
Unloaded 事件中销毁VLC对象
_mediaPlayer.Stop();
_mediaPlayer.Dispose();
_libVlc.Dispose();
播放RTSP
var options = new[]
{
"file-caching=300",
"live-capture-caching=300",
"disc-caching-caching=300",
"network-caching=333",
"live-caching=300"
};
using (var media = new Media(_libVlc, new Uri(CameraPara.Url), options))
{
VideoView.MediaPlayer.Play(media);
}
停止播放
if (VideoView.MediaPlayer.IsPlaying)
{
VideoView.MediaPlayer.Stop();
}
2、Vlc.DotNet.Wpf
借助于VlcVideoSourceProvider类,可以将RTSP视频流经过处理后轻松绑定到Image对象。
同时,通过改造VlcVideoSourceProvider类可以实现自由设置RTSP视频流的播放帧率(FPS)。
引入NuGet包
Vlc.DotNet.Wpf。
启用位图缓存
<UserControl.CacheMode>
<BitmapCache/>
</UserControl.CacheMode>
初始化SourceProvider
_sourceProvider = new VlcVideoSourceProvider(Dispatcher);
_sourceProvider.CreatePlayer(_libDirectory, "--no-audio", "--rtsp-tcp");
_sourceProvider.MediaPlayer.Stopped += OnPlayerStopped;
VideoImage.SetBinding(Image.SourceProperty,
new Binding(nameof(VlcVideoSourceProvider.VideoSource))
{
Source = _sourceProvider
});
播放RTSP视频流
var options = new[]
{
"file-caching=300",
"live-capture-caching=300",
"disc-caching-caching=300",
"network-caching=333",
"live-caching=300",
HardDecoding ? "avcodec-hw=any" : "" //硬解码
};
_sourceProvider.MediaPlayer.Play(new Uri(CameraPara.Url), options);
停止播放
if (_sourceProvider.MediaPlayer.IsPlaying())
{
_sourceProvider?.MediaPlayer?.Dispose();
_sourceProvider?.Dispose();
_sourceProvider = null;
}
3、LibVLCSharp + SkiaSharp
通过设置MediaPlayer的视频回调方法,在视频VideoLock回调中将视频图像拷贝至缓存中,然后在VideoDisplay回调中将缓存中的数据转换为SKImage绘制在Skia画布中,之后刷新关联的位图即可。
引入NuGet包
LibVLCSharp、SkiaSharp。
初始化LibVLCSharp
LibVLCSharp.Shared.Core.Initialize();
初始化LibVLC对象
_libVlc = new LibVLC(true);
_mediaPlayer = new MediaPlayer(_libVlc)
{
Volume = 0, //静音
EnableHardwareDecoding = true //硬件加速
};
_mediaPlayer.EndReached += OnPlayerEndReached;
_mediaPlayer.Stopped += OnPlayerStopped;
Loaded事件中设置视频回调
_mediaPlayer.SetVideoFormatCallbacks(OnLibVLCVideoFormat, null);
_mediaPlayer.SetVideoCallbacks(OnLibVLCVideoLock, null, OnLibVLCVideoDisplay);
Unloaded事件中销毁VLC对象
_mediaPlayer.Stop();
_mediaPlayer.Dispose();
_libVlc.Dispose();
视频回调处理
private uint OnLibVLCVideoFormat(ref IntPtr opaque, IntPtr chroma,
ref uint width, ref uint height, ref uint pitches, ref uint lines)
// ReSharper restore RedundantAssignment
{
var bytes = Encoding.ASCII.GetBytes("RV32"); //I420, RV32, AVC1
for (var i = 0; i < bytes.Length; i++)
{
Marshal.WriteByte(chroma, i, bytes[i]);
}
if (_mediaPlayer.Media is Media media)
{
foreach (MediaTrack track in media.Tracks)
{
if (track.TrackType == TrackType.Video)
{
var trackInfo = track.Data.Video;
if (trackInfo.Width > 0 && trackInfo.Height > 0)
{
width = trackInfo.Width;
height = trackInfo.Height;
}
break;
}
}
}
var pixelFormat = PixelFormats.Bgra32;
pitches = (uint) (width * pixelFormat.BitsPerPixel) / 8;
lines = height;
_videoWidth = (int) width;
_videoHeight = (int) height;
_buffer = new byte[_videoWidth * _videoHeight * 4];
_plane = Marshal.UnsafeAddrOfPinnedArrayElement(_buffer, 0);
Dispatcher.Invoke(delegate
{
_bitmap = new WriteableBitmap(_videoWidth, _videoHeight, 96, 96, PixelFormats.Bgra32, null);
_imageInfo = new SKImageInfo(_videoWidth, _videoHeight, SKColorType.Bgra8888);
_surface = SKSurface.Create(
new SKImageInfo(_videoWidth, _videoHeight, SKImageInfo.PlatformColorType, SKAlphaType.Premul),
_bitmap.BackBuffer, _bitmap.BackBufferStride);
_rect = new Int32Rect(0, 0, _videoWidth, _videoHeight);
VideoImage.Source = _bitmap;
VideoImage.Stretch = IsMainControl ? Stretch.Fill : Stretch.Uniform;
});
return 1;
}
private IntPtr OnLibVLCVideoLock(IntPtr opaque, IntPtr planes)
{
IntPtr[] dataArray = {_plane};
Marshal.Copy(dataArray, 0, planes, dataArray.Length);
return IntPtr.Zero;
}
private void OnLibVLCVideoDisplay(IntPtr opaque, IntPtr picture)
{
var image = SKImage.FromPixels(_imageInfo, _plane);
_surface.Canvas.DrawImage(image, new SKPoint(0, 0));
Dispatcher.Invoke(delegate
{
_bitmap.Lock();
_bitmap.AddDirtyRect(_rect);
_bitmap.Unlock();
});
}
播放RTSP视频
var options = new[]
{
"file-caching=300",
"live-capture-caching=300",
"disc-caching-caching=300",
"network-caching=333",
"live-caching=300"
};
using (var media = new Media(_libVlc, new Uri(CameraPara.Url), options))
{
_mediaPlayer.Play(media);
}
停止播放
if (_mediaPlayer.IsPlaying)
{
_mediaPlayer.Stop();
}
总结
本文介绍了在WPF中播放RTSP视频流的几种实现方法,包括使用开源库和第三方工具。每种方法都有其优缺点,可以根据具体需求选择合适的方案。通过本文的介绍,大家可以快速掌握RTSP视频流播放的核心技术,提升开发效率。
作者:xhubobo
觉得有收获?不妨分享让更多人受益
关注「DotNet技术匠」,共同提升技术实力