我使用UnrealEngine 4.10(UE4)创建了一个小应用程序。在该应用程序中,我通过ReadPixels抓取colorBuffer。然后我将colorBuffer压缩为PNG。最后,压缩的colorBuffer通过TCP发送到另一个应用程序。 UE4应用程序是用c ++编写的,而不是重要的。 压缩的colorBuffer在应用程序的每个“tick”发送 - 基本上每秒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;
}
问题在于Image img = (Image)converter.ConvertFrom(byteArrayIn);
引发争论异常,告诉我“ Parmeter无效”。
我也试过了两个:
Image.FromStream(stream);
和Image.FromStream(new MemoryStream(bytes));
Image.FromStream(stream);
会使其永久阅读...而Image.FromStream(new MemoryStream(bytes));
会导致与上述相同的异常。
有些问题:
我应该将BYTES_TO_READ
设置为什么尺寸?我设置为16因为当我检查UE4应用程序中发送的字节数组的大小(第一张图像中为dataSize
)时,它表示长度为16 ...不太确定将此设置为
我所遵循的流程是否正确?
我做错了什么?
更新
@RonBeyer询问我是否可以验证从服务器发送的数据是否与收到的数据相匹配。我试过这样做,这就是我能说的:
收到的数据如下:
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
如果我尝试从responseData中取一行并将其放入图像中:
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);
data
的长度为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);
}
有了这个,我现在正在第一次获取图像,但是,后续尝试失败并且相同的“参数无效”。检查后,我注意到流现在似乎缺少标题... "?PNG\r\n\u001a\n\0\0\0\rIHDR"
。当我使用Encoding.ASCII.GetString(buffer, 0, bytes);
知道为什么标题现在只是第一次发送而不再发送?我该怎么做才能解决它?
答案 0 :(得分:0)
您只读取流的前16个字节。我猜这不是故意的。
如果在传输图像后流关闭/连接关闭,请使用stream.CopyTo
将其复制到MemoryStream
。 Image.FromStream(stream)
也可能有用
如果流没有结束,您需要事先知道传输对象的大小,因此您可以将其read-by-read复制到另一个阵列/内存流或直接复制到磁盘。在这种情况下,应该使用更高的读缓冲区(我认为默认值是8192)。但这要复杂得多。
要从流中手动读取,您需要在数据前加上大小。一个简单的Int32就足够了。您的客户端代码可能如下所示:
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 );
我在这里掩饰了很多错误处理,但这应该给你一般的想法。
如果您想传输更复杂的对象,请查看适当的协议,例如Google Protocol Buffers或MessagePack
答案 1 :(得分:0)
首先,感谢@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);
}
第一个问题是图像的大小需要正确:
int32 SendSize = 2 * x * y;
以上一行是错误的。图片为512 by 512
,因此SendSize
应为x * y
,其中x&amp; y都是512。
另一个问题是我如何处理流客户端。
<强>客户端:强>
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流式传输它的帧。