C# 将流式PNG数据转换为图像:使用UnrealEngine 4.10通过TCP流式传输的数据
我使用UnrealEngine 4.10(UE4)创建了一个小应用程序。在该应用程序中,我通过ReadPixels获取colorBuffer。然后我将colorBuffer压缩为PNG。最后,压缩的colorBuffer通过TCP发送到另一个应用程序。UE4应用程序是用C++编写的,而这并不重要。strong>压缩的colorBuffer在应用程序的每一个“滴答声”中发送一次,基本上每秒发送30次(大约30次)。 我的客户机,接收流式PNG的客户机是用c#编写的,并执行以下操作:C# 将流式PNG数据转换为图像:使用UnrealEngine 4.10通过TCP流式传输的数据,c#,tcp,streaming,unreal-engine4,C#,Tcp,Streaming,Unreal Engine4,我使用UnrealEngine 4.10(UE4)创建了一个小应用程序。在该应用程序中,我通过ReadPixels获取colorBuffer。然后我将colorBuffer压缩为PNG。最后,压缩的colorBuffer通过TCP发送到另一个应用程序。UE4应用程序是用C++编写的,而这并不重要。strong>压缩的colorBuffer在应用程序的每一个“滴答声”中发送一次,基本上每秒发送30次(大约30次)。 我的客户机,接收流式PNG的客户机是用c#编写的,并执行以下操作: 连接到服务器
- 连接到服务器(如果已连接)
- 获取流(内存流)
- 将内存流读入字节数组
- 将字节数组转换为图像
private void Timer_Tick(object sender, EventArgs e)
{
var connected = tcp.IsConnected();
if (connected)
{
var stream = tcp.GetStream(); //simply returns client.GetStream();
int BYTES_TO_READ = 16;
var buffer = new byte[BYTES_TO_READ];
var totalBytesRead = 0;
var bytesRead;
do {
// You have to do this in a loop because there's no
// guarantee that all the bytes you need will be ready when
// you call.
bytesRead = stream.Read(buffer, totalBytesRead,
BYTES_TO_READ - totalBytesRead);
totalBytesRead += bytesRead;
} while (totalBytesRead < BYTES_TO_READ);
Image x = byteArrayToImage(buffer);
}
}
public Image byteArrayToImage(byte[] byteArrayIn)
{
var converter = new ImageConverter();
Image img = (Image)converter.ConvertFrom(byteArrayIn);
return img;
}
如果我尝试从响应数据中提取一行并将其放入图像中:
var stringdata = "?PNG\r\n\u001a\n\0\0\0\rIHDR";
var data = System.Text.Encoding.ASCII.GetBytes(stringdata);
var ms = new MemoryStream(data);
Image img = Image.FromStream(ms);
数据的长度为16。。。与服务器上的dataSize
变量长度相同。但是,我再次得到执行选项“参数无效”
更新2
@Darcara建议我实际收到的是PNG文件的头,我需要首先发送图像的大小,这对我有所帮助。我现在已经这样做并取得了进展:
for (TArray<class FSocket*>::TIterator ClientIt(Clients); ClientIt;
++ClientIt)
{
FSocket *Client = *ClientIt;
int32 SendSize = 2 * x * y;
Client->SetNonBlocking(true);
Client->SetSendBufferSize(SendSize, SendSize);
Client->Send(data, SendSize, bytesSent);
}
for(TArray::滴度仪ClientIt(客户端);ClientIt;
++(客户)
{
FSocket*Client=*ClientIt;
int32 SendSize=2*x*y;
客户端->设置非阻塞(true);
客户端->设置SendBufferSize(SendSize,SendSize);
客户端->发送(数据、发送大小、bytesSent);
}
有了这个,我现在第一次就得到了图像,但是,随后的尝试失败了,并且出现了相同的“参数无效”。经过检查,我注意到流现在似乎缺少标题<代码>“?PNG\r\n\u001a\n\0\0\0\rIHDR”
。当我使用Encoding.ASCII.GetString(buffer,0,bytes)将缓冲区转换为字符串时,我得出了这个结论代码>
你知道为什么报头现在只被第一次发送而不再发送吗?我能做些什么来修复它?您只读取流的前16个字节。我猜这不是故意的
如果图像传输后流结束/连接关闭,请使用stream.CopyTo
将其复制到MemoryStream
<代码>图像。FromStream(stream)
也可以工作
如果流没有结束,您需要事先知道传输对象的大小,以便可以通过读取将其复制到另一个阵列/内存流或直接复制到磁盘。在这种情况下,应该使用更高的读取缓冲区(我认为默认值是8192)。但这要复杂得多
要手动从流中读取数据,需要在数据前面加上大小。一个简单的Int32就足够了。您的客户端代码可能如下所示:
var stream = tcp.GetStream();
int BYTES_TO_READ = 512;
var buffer = new byte[BYTES_TO_READ];
Int32 bytes = stream.Read(buffer, 0, buffer.Length);
var responseData = System.Text.Encoding.ASCII.GetString(buffer, 0,
bytes);
//responseData looks like this (has been formatted for easier reading)
//?PNG\r\n\u001a\n\0\0\0\rIHDR
//?PNG\r\n\u001a\n\0\0\0\rIHDR
//?PNG\r\n\u001a\n\0\0\0\rIHDR
//?PNG\r\n\u001a\n\0\0\0\rIHDR
//?PNG\r\n\u001a\n\0\0\0\rIHDR
//?PNG\r\n\u001a\n\0\0\0\rIHDR
//?PNG\r\n\u001a\n\0\0\0\rIHDR
//?PNG\r\n\u001a\n\0\0\0\rIHDR
//?PNG\r\n\u001a\n\0\0\0\rIHDR
//?PNG\r\n\u001a\n\0\0\0\rIHDR
var stream = tcp.GetStream();
//this is our temporary read buffer
int BYTES_TO_READ = 8196;
var buffer = new byte[BYTES_TO_READ];
var bytesRead;
//read size of data object
stream.Read(buffer, 0, 4); //read 4 bytes into the beginning of the empty buffer
//TODO: check that we actually received 4 bytes.
var totalBytesExpected = BitConverter.ToInt32(buffer, 0)
//this will be the stream we will save our received bytes to
//could also be a file stream
var imageStream = new MemoryStream(totalBytesExpected);
var totalBytesRead = 0;
do {
//read as much as the buffer can hold or the remaining bytes
bytesRead = stream.Read(buffer, 0, Math.Min(BYTES_TO_READ, totalBytesExpected - totalBytesRead));
totalBytesRead += bytesRead;
//write bytes to image stream
imageStream.Write(buffer, 0, bytesRead);
} while (totalBytesRead < totalBytesExpected );
var-stream=tcp.GetStream();
//这是我们的临时读取缓冲区
int BYTES_至_READ=8196;
var buffer=新字节[字节读取];
var字节读取;
//数据对象的读取大小
读取(缓冲区,0,4)//将4个字节读入空缓冲区的开头
//TODO:检查我们是否实际收到了4个字节。
var totalBytesExpected=BitConverter.ToInt32(缓冲区,0)
//这将是我们将接收到的字节保存到的流
//也可以是文件流
var imageStream=新的内存流(预计为TotalBytes);
var totalBytesRead=0;
做{
//尽可能多地读取缓冲区中的数据或剩余字节
bytesRead=stream.Read(缓冲区,0,Math.Min(字节从0到0读取,totalBytesExpected-totalBytesRead));
totalBytesRead+=字节读取;
//将字节写入图像流
写入(缓冲区,0,字节读取);
}而(totalBytesRead
我在这里忽略了很多错误处理,但这应该给你一个大致的想法
如果您想传输更复杂的对象,请查看适当的协议,如or,首先感谢@Dacara和@RonBeyer的帮助 我现在有了一个解决方案: 服务器:
for (TArray<class FSocket*>::TIterator ClientIt(Clients); ClientIt;
++ClientIt)
{
FSocket *Client = *ClientIt;
int32 SendSize = x * y; // Image is 512 x 512
Client->SetSendBufferSize(SendSize, SendSize);
Client->Send(data, SendSize, bytesSent);
}
var connected = tcp.IsConnected();
if (connected)
{
var stream = tcp.GetStream();
var BYTES_TO_READ = (512 * 512)^2;
var buffer = new byte[BYTES_TO_READ];
var bytes = stream.Read(buffer, 0, BYTES_TO_READ);
Image returnImage = Image.FromStream(new MemoryStream(buffer));
//Apply the image to a picture box. Note, this is running on a separate
//thread.
UpdateImageViewerBackgroundWorker.ReportProgress(0, returnImage);
}
上面这行是错的。图像是512 x 512
,因此SendSize
应该是x*y
,其中x和y都是512
另一个问题是我如何处理流客户端
客户端:
for (TArray<class FSocket*>::TIterator ClientIt(Clients); ClientIt;
++ClientIt)
{
FSocket *Client = *ClientIt;
int32 SendSize = x * y; // Image is 512 x 512
Client->SetSendBufferSize(SendSize, SendSize);
Client->Send(data, SendSize, bytesSent);
}
var connected = tcp.IsConnected();
if (connected)
{
var stream = tcp.GetStream();
var BYTES_TO_READ = (512 * 512)^2;
var buffer = new byte[BYTES_TO_READ];
var bytes = stream.Read(buffer, 0, BYTES_TO_READ);
Image returnImage = Image.FromStream(new MemoryStream(buffer));
//Apply the image to a picture box. Note, this is running on a separate
//thread.
UpdateImageViewerBackgroundWorker.ReportProgress(0, returnImage);
}
var BYTES-TO-READ=(512*512)^2代码>现在是正确的大小
我现在有了“虚幻引擎4”流式传输其帧。我不确定这是否正确使用了ImageConverter
。你试过仅仅从字节数组构造的内存流
创建图像
吗?@RonBeyer你能详细说明一下吗?如上所述,我尝试了Image.FromStream。这就是你的意思吗?我向FromStream传递了一个内存流。是的,这就是我的意思。当你试着那样做的时候,你有没有遇到一个例外?另外,您是否验证了接收到的数据与发送的数据匹配,并且没有进行字节交换或其他操作?@RonBeyer我在尝试Image.FromStream(stream)时没有收到任何执行选项。断点命中它,然后什么也没有发生。。。尽管在计时器滴答声事件中,并且每100毫秒调用一次。我不确定如何验证发送的数据是否是接收的数据-我尝试过这样做,请查看更新的问题。@RonBeyer我忘了提到客户机,除了在断点后没有任何事情发生之外,变得没有响应。我尝试了您的建议,并且var imageStream=new MemoryStream(totalBytesExpected)代码>抛出OutOfMemoryException;(即,代码< ToByTeSeRealEngult> /COD>太大。您不是在将PNG从C++代码中发送之前直接发送PNG的大小,还是DE /序列化失败(DeNANESS?)。”代码> ToByTeSeRealsIuth> /Cuth>有什么值?@ DaCARI我尝试改变大小,但总是出错。