一款高效视频播放控件的设计思路(c# WPF版)
2021-07-09 23:10
标签:res fir 服务器 技术分享 flags 类型 证明 公司 elf 因工作的需要,开发了一款视频播放程序。期间也经历许多曲折,查阅了大量资料,经过了反复测试,终于圆满完成了任务。 我把开发过程中的一些思路、想法写下来,以期对后来者有所帮助。 视频播放的本质 就是连续的图片。当每秒播放的图片超过一定数量,人眼就很难觉察到每帧图像播放间隔,看到的就是连续的视频流。 视频播放的过程 必须有数据源,数据源一般是摄像头采集后,再经过压缩传送到程序。摄像头采集的视频信号一般转换为YUV格式、这个格式再经过h264压缩,传送出去。(视频信号不经过压缩,数据量非常大,h264是当今最流行的压缩格式) 程序处理的过程要经过相反的过程。先对h264解压缩获取YUV格式数据,再将YUV格式数据转换为RGB格式。视频控件的功能就是高效的把RGB数据显示出来。后续主要介绍这个处理流程。 h264解压缩采用的ffmpeg库,如何处理解压缩见我另一篇文章:使用ffmpeg实现对h264视频解码。YUV格式转换为RGB格式的原理并不复杂,关键是转换效率,一般的算法占用CPU非常高,我这里也是采用ffmpeg库提供的转换算法。 视频播放代码解析 1)播放视频的本质就是rgb数据的高效显示。播放控件输入数据如下: RgbData数据就是ffmpeg解压缩后的数据,该数据根据播放控件的大小,对视频做了缩放。 2)RGB数据转换 视频播放使用WPF Image控件,对此控件做了进一步封装。设置Image.Source属性,就可以显示图片。Source属性的类型是ImageSource。RGB数据必须转换为ImageSource类型,才能对Image.Source赋值。这里,我是把RGB数据使用WriteableBitmap封装,能很好的实现这个功能。 将WriteableBitmap赋值给Image.Source,就能显示图片了。还有几点需要注意:对界面控件的赋值必须在界面线程处理: 3)数据缓冲和精确定时 视频数据的来源不可能是均匀连续的,需要对数据做缓冲,再均匀连续的播放出来。需要将数据放到缓冲类中,每隔固定的时间去取。 现在假定播放帧数为25帧每秒。该缓冲类有自适应功能,就是缓冲数据帧数小于一定值时,播放变慢;否则,播放变快。 需要一个精确时钟,每隔一段时间从缓冲区取数据,再将数据显示出来。Windows下多媒体时钟精度较高,定时器代码如下: 总结: 后记: 一款高效视频播放控件的设计思路(c# WPF版) 标签:res fir 服务器 技术分享 flags 类型 证明 公司 elf 原文地址:https://www.cnblogs.com/yuanchenhui/p/videoplay.html public class BitmapDecodeInfo
{
public VideoClientTag ClientTag; //视频源唯一标识
public Size OrginSize; //视频原始大小
public Size CurSize; //视频当前大小
public int Framerate; //每秒播放帧数
public byte[] RgbData { get; internal set; } //视频数据
}
WriteableBitmap _drawBitmap;
public WriteableBitmap GetWriteableBitmap(BitmapDecodeInfo bitmapInfo, out bool newBitmap)
{
if (_drawBitmap == null
|| _drawBitmap.Width != bitmapInfo.CurSize.Width
|| _drawBitmap.Height != bitmapInfo.CurSize.Height)
{
newBitmap = true;
_drawBitmap = new WriteableBitmap(bitmapInfo.CurSize.Width, bitmapInfo.CurSize.Height, 96, 96, PixelFormats.Bgr24, null);
_drawBitmap.WritePixels(new Int32Rect(0, 0, bitmapInfo.CurSize.Width, bitmapInfo.CurSize.Height),
bitmapInfo.RgbData, _drawBitmap.BackBufferStride, 0);
return _drawBitmap;
}
else
{
newBitmap = false;
_drawBitmap.WritePixels(new Int32Rect(0, 0, bitmapInfo.CurSize.Width, bitmapInfo.CurSize.Height),
bitmapInfo.RgbData, _drawBitmap.BackBufferStride, 0);
return _drawBitmap;
}
}
Dispatcher.Invoke(new Action(() =>
{
if (AppValue.WpfShowImage)
{
BitmapSource source = GetWriteableBitmap(bitmapInfo, out bool newBitmap);
if (newBitmap)
Source = source;
}
}));
//图像缓冲,播放速度控制
public class ImageVideoPool
{
public long _spanPerFrame = 40; //时间间隔 毫秒。每秒25帧
public long _spanPerFrameCur = 40;
public void SetFramerate(int framerate)
{
_spanPerFrame = 1000 / framerate;
_spanPerFrameCur = _spanPerFrame;
}
ObjectPool
class MMTimer
{
//Lib API declarations
[DllImport("Winmm.dll", CharSet = CharSet.Auto)]
static extern uint timeSetEvent(uint uDelay, uint uResolution, TimerCallback lpTimeProc, UIntPtr dwUser,
uint fuEvent);
[DllImport("Winmm.dll", CharSet = CharSet.Auto)]
static extern uint timeKillEvent(uint uTimerID);
[DllImport("Winmm.dll", CharSet = CharSet.Auto)]
static extern uint timeGetTime();
[DllImport("Winmm.dll", CharSet = CharSet.Auto)]
static extern uint timeBeginPeriod(uint uPeriod);
[DllImport("Winmm.dll", CharSet = CharSet.Auto)]
static extern uint timeEndPeriod(uint uPeriod);
//Timer type definitions
[Flags]
public enum fuEvent : uint
{
TIME_ONESHOT = 0, //Event occurs once, after uDelay milliseconds.
TIME_PERIODIC = 1,
TIME_CALLBACK_FUNCTION = 0x0000, /* callback is function */
//TIME_CALLBACK_EVENT_SET = 0x0010, /* callback is event - use SetEvent */
//TIME_CALLBACK_EVENT_PULSE = 0x0020 /* callback is event - use PulseEvent */
}
//Delegate definition for the API callback
delegate void TimerCallback(uint uTimerID, uint uMsg, UIntPtr dwUser, UIntPtr dw1, UIntPtr dw2);
//IDisposable code
private bool disposed = false;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (!this.disposed)
{
Stop();
}
disposed = true;
}
~MMTimer()
{
Dispose(false);
}
///
把视频控件处理流程梳理一下:1视频数据放入缓冲-->定时器每隔一端时间取出数据-->将数据显示到image控件上。
交通部2016年发布一个规范《JT/T1076-2016道路运输车辆卫星定位系统车载视频终端技术要求》,规范的目的是一个平台可以接入多个硬件厂家的视频数据。本人就是依据这个规范开发的系统。视频解码采用ffmpeg开源库。整个系统包括视频数据采集、流媒体服务器、视频播放器。所有程序采用c#编写。视频数据的数据量一般都很大;所以,在开发过程中,十分注重性能。
有些人对c#的性能有些担忧的,毕竟市面上的流媒体服务器、播放器大部分都是c语言编写的。我从事c#开发10多年,认为c#性能上是没有问题,关键还是个人要对算法有所了解,对所处理的逻辑有所了解。一切都是拿来主义,性能肯定不会高。开发本系统中,好多处理算法都是自己从头编写。事实证明,c#也可以开发出高效的系统。
我大概用了3个月,把整个系统设计完成。可惜,因为市场的原因,公司不打算在这个方向投入。本人因此离职。现处于求职中!
下一篇:windows 命令行切换目录
文章标题:一款高效视频播放控件的设计思路(c# WPF版)
文章链接:http://soscw.com/index.php/essay/102966.html