【发布时间】:2018-02-02 17:24:54
【问题描述】:
在我的项目中,我需要通过 UDP 接收视频。 Source 有一个 IP 224.0.0.21,Sink 有一个 IP 169.254.170.141。我通过端口 3956 接收视频(这是来自 Wireshark 的有效信息)。我使用 SharpPcap 接收 UDP 流量,但它没有加入多播的方法。我从 MSDN 尝试了这段代码,但它不起作用。
IPAddress multicastaddress = IPAddress.Parse("224.0.0.21");
IPEndPoint remoteep = new IPEndPoint(IPAddress.Any, 3956);
m_ClientTarget.JoinMulticastGroup(multicastaddress, localAddr);
在我的 PC 中,我有一些网络适配器,但我使用连接到源视频的设备的 IP 地址。源和接收器直接连接。当我开始在 Wireshark 中监控流量时,我的程序也会收到数据包,但如果没有 Wireshack,它就无法做到这一点。 我的代码:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using SharpPcap;
using SharpPcap.LibPcap;
using SharpPcap.AirPcap;
using SharpPcap.WinPcap;
using System.IO;
using System.Net.Sockets;
using System.Net;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Threading;
namespace GVSPCapture
{
public partial class Form1 : Form
{
static int frameCounter = 0;
static byte[] pixels = new byte[1920 * 1080 * 3];
static IPAddress fpgaAddr = IPAddress.Parse("224.0.0.21");
static IPAddress localAddr = IPAddress.Parse("169.254.170.141");
static MulticastOption mcastOption = new MulticastOption(fpgaAddr, localAddr);
private static UdpClient m_ClientTarget = new UdpClient(3956);
private static IPAddress m_GrpAddr;
const int GroupPort = 3956;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
findDevices();
}
public void findDevices()
{
string ver = SharpPcap.Version.VersionString;
var devices = CaptureDeviceList.Instance;
foreach (var dev in devices)
{
lbxDevices.Items.Add(dev.Name + dev.Description);
}
}
private void JoinVideoMulticast()
{
IPAddress multicastaddress = IPAddress.Parse("224.0.0.21");
IPEndPoint remoteep = new IPEndPoint(IPAddress.Any, 3956);
m_ClientTarget.JoinMulticastGroup(multicastaddress, IPAddress.Parse("169.254.170.141"));
while (true)
{ }
}
private void startCapture(ICaptureDevice dev)
{
if (!dev.Started)
{
dev.OnPacketArrival += new PacketArrivalEventHandler(device_OnPacketArrival);
int readTimeoutMilliseconds = 1000;
if (dev is AirPcapDevice)
{
// NOTE: AirPcap devices cannot disable local capture
var airPcap = dev as AirPcapDevice;
airPcap.Open(SharpPcap.WinPcap.OpenFlags.DataTransferUdp, readTimeoutMilliseconds);
}
else if (dev is WinPcapDevice)
{
var winPcap = dev as WinPcapDevice;
winPcap.Open(SharpPcap.WinPcap.OpenFlags.DataTransferUdp | SharpPcap.WinPcap.OpenFlags.NoCaptureLocal, readTimeoutMilliseconds);
}
else if (dev is LibPcapLiveDevice)
{
var livePcapDevice = dev as LibPcapLiveDevice;
livePcapDevice.Open(DeviceMode.Promiscuous, readTimeoutMilliseconds);
}
else
{
throw new System.InvalidOperationException("unknown device type of " + dev.GetType().ToString());
}
dev.StartCapture();
Thread recvThread = new Thread(JoinVideoMulticast);
recvThread.Start();
}
}
delegate void SetTextCallback(string text);
private void SetText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.tbxCnt.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.tbxCnt.Text = text;
}
}
private void device_OnPacketArrival(object sender, CaptureEventArgs e)
{
var time = e.Packet.Timeval.Date;
var len = e.Packet.Data.Length;
if (len == 572)
{
var tmp = e.Packet.Data;
int packet_id = tmp[47] << 16 | tmp[48] << 8 | tmp[49];
int startPos = (packet_id - 1) * 522;
for (int i = 50; i < tmp.Length; i+=3)
{
pixels[startPos + i + 0 - 50] = tmp[i];
pixels[startPos + i + 1 - 50] = tmp[i];
pixels[startPos + i + 2 - 50] = tmp[i];
}
}
if (len == 60)
{
var im = CopyDataToBitmap(pixels);
pictbFrame.Image = im;
frameCounter += 1;
SetText(frameCounter.ToString());
}
}
public Bitmap CopyDataToBitmap(byte[] data)
{
GCHandle pinned = GCHandle.Alloc(data, GCHandleType.Pinned);
IntPtr ptr = pinned.AddrOfPinnedObject();
BitmapData dt = new BitmapData();
dt.Scan0 = ptr;
dt.Stride = 5760;
dt.Width = 1920;
dt.Height = 1080;
dt.PixelFormat = PixelFormat.Format24bppRgb;
Bitmap btm = new Bitmap(1920, 1080, 5760, PixelFormat.Format24bppRgb, dt.Scan0);
return btm;
}
private void btnStart_Click(object sender, EventArgs e)
{
int devNum = lbxDevices.SelectedIndex;
if (devNum > 0)
{
var device = CaptureDeviceList.Instance[devNum];
startCapture(device);
}
}
private void btnStop_Click(object sender, EventArgs e)
{
int devNum = lbxDevices.SelectedIndex;
if (devNum > 0)
{
var device = CaptureDeviceList.Instance[devNum];
if (device.Started)
{
device.StopCapture();
device.Close();
}
}
m_ClientTarget.DropMulticastGroup(fpgaAddr);
}
}
}
【问题讨论】:
-
一个问题是多播地址是为路由器功能保留的,您一开始不应该尝试在 PC 上加入它。
-
@RonMaupin 加入多播地址有什么不好?
-
如果多播组被保留用于其他目的,这很糟糕。该多播组不应该用于视频。为此类事物保留了一组多播地址(
239.0.0.0-239.255.255.255Organization-Local Scope)。这就像试图将属于 Google 的 IP 地址用于您自己的目的一样。 IANA 维护 IPv4 Multicast Address Space Registry 的方式与维护单播地址分配的方式相同。 -
顺便说一下,多播地址是目标地址,而不是源地址。
-
@RonMaupin 加入多播地址有什么不好? OP 没有说他/她将数据发送到该地址。视频源可以是用户无法控制的任何内容。
标签: c# sockets networking sharppcap