我想使用UDP连接从客户端发送视频帧到服务器。
如何从网络摄像头获取视频帧?我是否需要将每个帧保存到我的计算机并从那里发送到服务器?如果我以30fps运行,这意味着我需要每秒在计算机上保存30个视频帧?或者我可以直接发送从网络摄像头获得的视频帧吗?
目前,我只能打开网络摄像头,并使用Aforge库将视频显示在客户端的UI上。
我查看了许多不同的主题,但我似乎并没有从中受益,因为我在C#中的基础薄弱。
提前致谢!
答案 0 :(得分:1)
您可以使用DirectShow.NET库(可通过NuGet将其作为DirectShowLib提供)逐帧捕获网络摄像头视频。这是一个很好的DxSnap示例,展示了如何获取静态图像。最困难的部分是了解DirectShow API,如果你在C#中有一个薄弱的基础,它可能看起来很陌生。和/或之前没有处理COM代码。
根据分辨率,由于性能/带宽限制,帧速率可能会低于30 fps。如果您需要具有良好分辨率的高帧速率,则可能必须首先使用一些比特率高效的编码器对视频进行编码。
这两个选项 - 逐帧和视频编码 - 都可以使用这个库(虽然后者可能不容易通过UDP)。您只需要设置一个合适的图表。 GraphEditPlus可能对此有很大的帮助,特别是因为它可以为DirectShow.NET生成图形的C#代码(这里有点烦恼是你不能将代码复制到剪贴板,除非你购买了应用程序)。
基本上,如果您确定要发送每一帧,那么您只需要构建如下图形:视频捕获源>样品采集器>空渲染器。您可以设置示例抓取器以将每个帧重定向到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();
}
}