c#发送视频帧
本文关键字:视频 | 更新日期: 2023-09-27 17:51:24
我想使用UDP连接从客户端到服务器发送视频帧。
如何从我的网络摄像头获取视频帧?我是否需要将每一帧保存到我的计算机上,然后再将其发送到服务器?如果我以30fps运行,这意味着我需要在电脑上每秒保存30个视频帧?或者我可以直接从我从网络摄像头获得的视频帧发送吗?
目前我只能打开网络摄像头,并使用Aforge库将视频显示到客户端的UI上。
我看了很多不同的线程,我似乎没有从中受益,因为我的c#基础薄弱。
提前感谢!
您可以使用DirectShow逐帧捕获网络摄像头视频。. NET库(在NuGet上作为DirectShowLib可用)。有一个很好的DxSnap示例展示了如何获得静止图像。最难的部分是理解DirectShow API,如果你"c#基础薄弱"和/或以前没有处理过COM代码,那么DirectShow API可能看起来很陌生。
根据分辨率,由于性能/带宽限制,帧率可能低于30 fps。如果你需要一个高帧率和一个像样的分辨率,你可能必须先使用一些比特率高效的编码器对视频进行编码。
两个选项-逐帧和视频编码-都可以使用这个库(尽管后者可能不容易通过UDP)。你只需要建立一个合适的图形。graphheditplus可能在这方面有很大的帮助,特别是因为它可以为DirectShow生成图形的c#代码。. NET(这里有点恼人的是,除非你购买了应用程序,否则你无法将代码复制到剪贴板上)。
基本上,如果你确定发送每一帧,那么你所需要的就是建立一个像这样的图表:视频捕获源>样本采集器> Null Renderer。您设置采样采集器将每个帧重定向到ISampleGrabberCB
的实现,并且在BufferCB
方法中,您将获得指向帧的位图数据的指针。下面是一些代码,我试图尽可能地保持简短,从而牺牲了它的健壮性。看一看,但请不要把它留在生产中,如果它是有效的。
class FrameGrabber : ISampleGrabberCB
{
IMediaControl mediaCtrl;
int width, height, stride;
public FrameGrabber(DsDevice camDevice)
{
IFilterGraph2 filterGraph;
ICaptureGraphBuilder2 graphBuilder;
IBaseFilter camBase, nullRenderer;
ISampleGrabber sampleGrabber;
filterGraph = new FilterGraph() as IFilterGraph2;
mediaCtrl = filterGraph as IMediaControl;
graphBuilder = new CaptureGraphBuilder2() as ICaptureGraphBuilder2;
HRCheck(graphBuilder.SetFiltergraph(filterGraph));
// Add camera
HRCheck(filterGraph.AddSourceFilterForMoniker(
camDevice.Mon, null, camDevice.Name, out camBase));
// Add sample grabber
sampleGrabber = new SampleGrabber() as ISampleGrabber;
var mType = new AMMediaType()
{
majorType = MediaType.Video,
subType = MediaSubType.RGB24,
formatType = FormatType.VideoInfo
};
HRCheck(sampleGrabber.SetMediaType(mType));
DsUtils.FreeAMMediaType(mType);
HRCheck(sampleGrabber.SetCallback(this, 1));
HRCheck(filterGraph.AddFilter(sampleGrabber as IBaseFilter, "CamGrabber"));
// Add null renderer
nullRenderer = new NullRenderer() as IBaseFilter;
HRCheck(filterGraph.AddFilter(nullRenderer, "Null renderer"));
// Render the webcam through the grabber and the renderer
HRCheck(graphBuilder.RenderStream(PinCategory.Capture, MediaType.Video,
camBase, sampleGrabber as IBaseFilter, nullRenderer));
// Get resulting picture size
mType = new AMMediaType();
HRCheck(sampleGrabber.GetConnectedMediaType(mType));
if (mType.formatType != FormatType.VideoInfo || mType.formatPtr == IntPtr.Zero)
{
throw new NotSupportedException("Unknown grabber media format");
}
var videoInfoHeader = Marshal.PtrToStructure(mType.formatPtr,
typeof(VideoInfoHeader)) as VideoInfoHeader;
width = videoInfoHeader.BmiHeader.Width;
height = videoInfoHeader.BmiHeader.Height;
Console.WriteLine("{0} x {1}", width, height);
stride = width * (videoInfoHeader.BmiHeader.BitCount / 8);
DsUtils.FreeAMMediaType(mType);
HRCheck(mediaCtrl.Run());
}
public int BufferCB(double SampleTime, IntPtr pBuffer, int BufferLen)
{
Console.WriteLine("BufferCB: " + SampleTime.ToString());
// This is the bitmap of the frame but you may want
// to copy it to some other memory location to
// process/save/send it from there
var bmp = new Bitmap(width, height, stride,
PixelFormat.Format24bppRgb, pBuffer);
return 0;
}
public int SampleCB(double SampleTime, IMediaSample pSample)
{
// This won't be called because sampleGrabber.SetCallback(this, 1)
// -- 1 means BufferCB
return Marshal.ReleaseComObject(pSample);
}
static void HRCheck(int hr)
{
DsError.ThrowExceptionForHR(hr);
}
你可以这样称呼它:
class Program
{
static void Main(string[] args)
{
var cam = DsDevice.GetDevicesOfCat(FilterCategory.VideoInputDevice).First();
var grabber = new FrameGrabber(cam);
Console.ReadLine();
}
}