pookie
pookie

Reputation: 4142

System.Drawing.Image Parameter Not Valid

I know this has been asked numerous times... and I have searched and tried everything that I can, but I am still not sure why I get the "parameter is not valid" exception...

I have an Amazon EC2 instance running Windows Server 2012. On that machine, I am running Unity3D (Unity 5). That Unity application is sending frames (images) from the EC2 instance to my local laptop, via TCP. My client is running Windows 10, not that it is likely to make any difference.

To get my image data, I do the following:

byte[] GetScreenData() {
  // Create a texture the size of the screen, RGB24 format
  int width = Screen.width;
  int height = Screen.height;

  RenderTexture rt = new RenderTexture(width, height, 24);

  Texture2D tex = new Texture2D(width, height, TextureFormat.RGB24, false);

  Camera camera = GameObject.Find("Main Camera").GetComponent < Camera > ();
  camera.targetTexture = rt;
  camera.Render();

  RenderTexture.active = rt;

  // Read screen contents into the texture
  tex.ReadPixels(new Rect(0, 0, width, height), 0, 0);
  camera.targetTexture = null;
  RenderTexture.active = null;
  Destroy(rt);

  // Encode texture into JPG
  byte[] bytes = tex.EncodeToJPG();
  Destroy(tex);

  return bytes;
 }

I then serialize my data using FlatBuffers:

public static byte[] FlatSerialize(this ServerToClientMessage message) {
  var builder = new FlatBufferBuilder(1);

  //Create an ID
  var MessageId = builder.CreateString(message.MessageId.ToString());

  //Start the vector...
  //Loop over each byte and add it - my god, is there not a better way?
  FlatServerToClientMessage.StartImagebytesVector(builder, message.ImageBytes.Length);
  foreach(var imageByte in message.ImageBytes) {
   builder.AddByte(imageByte);
  }
  var imagebytes = builder.EndVector();

  //Start the FlatServerToClientMessage and add the MessageId and imagebytes
  FlatServerToClientMessage.StartFlatServerToClientMessage(builder);
  FlatServerToClientMessage.AddMessageid(builder, MessageId);
  FlatServerToClientMessage.AddImagebytes(builder, imagebytes);

  //End the FlatServerToClientMessage and finish it...
  var flatMessage = FlatServerToClientMessage.EndFlatServerToClientMessage(builder);
  FlatServerToClientMessage.FinishFlatServerToClientMessageBuffer(builder, flatMessage);

  return builder.SizedByteArray();
 }

Next, I send my data:

public void SendRaw(byte[] dataToSend) {
  ///We must send the length of the message before sending the actual message
  var sizeInfo = new byte[4]; // = BitConverter.GetBytes(dataToSend.Length);

  //Shift the bytes
  sizeInfo[0] = (byte) dataToSend.Length;
  sizeInfo[1] = (byte)(dataToSend.Length >> 8);
  sizeInfo[2] = (byte)(dataToSend.Length >> 16);
  sizeInfo[3] = (byte)(dataToSend.Length >> 24);

  try {
   var stream = Client.GetStream();

   //Send the length of the data
   stream.Write(sizeInfo, 0, 4);
   //Send the data
   stream.Write(dataToSend, 0, dataToSend.Length);
  } catch (Exception ex) {
   Debug.LogException(ex);
  } finally {
   //raise event to tell system that the client has disconnected and that listening must restart...
  }
 }

Back on my client device, I am listening for incoming data which deserializes and raises an event to alert the system to the arrival of a new image...

private void Run() {
  try {
   // ShutdownEvent is a ManualResetEvent signaled by
   // Client when its time to close the socket.
   while (!ShutDownEvent.WaitOne(0)) {
    try {
     if (!_stream.DataAvailable) continue;

     //Read the first 4 bytes which represent the size of the message, and convert from byte array to int32
     var sizeinfo = new byte[4];
     _stream.Read(sizeinfo, 0, 4);
     var messageSize = BitConverter.ToInt32(sizeinfo, 0);

     //create a new buffer for the data to be read
     var buffer = new byte[messageSize];

     var read = 0;
     //Continue reading from the stream until we have read all bytes @messageSize
     while (read != messageSize) {
      read += _stream.Read(buffer, read, buffer.Length - read);
     }

     var message = new ServerToClientMessage().FlatDeserialize(buffer);

     //raise data received event
     OnDataReceived(message);

    } catch (IOException ex) {
     // Handle the exception...
    }
   }
  } catch (Exception ex) {
   // Handle the exception...
  } finally {
   _stream.Close();
  }
 }

To deserialize, I do the following:

public static ServerToClientMessage FlatDeserialize(this ServerToClientMessage message, byte[] bytes) {

  var bb = new ByteBuffer(bytes);
  var flatmessage = FlatServerToClientMessage.GetRootAsFlatServerToClientMessage(bb);

  message.MessageId = new Guid(flatmessage.Messageid);
  message.ImageBytes = new byte[flatmessage.ImagebytesLength];

  for (var i = 0; i < flatmessage.ImagebytesLength; i++) {
   message.ImageBytes[i] = flatmessage.GetImagebytes(i);
  }

  return message;
 }

For clarity, here is the ServerToClientMessage class:

public class ServerToClientMessage : EventArgs
{
    public Guid MessageId { get; set; }
    public byte[] ImageBytes { get; set; }
}

Anyway, next, the OnDataReceived event gets raised and that in turn calls a function to convert from the ImageBytes array to a System.Drawing.Image. That function is here:

public Image byteArrayToImage(byte[] byteArrayIn) {
  // SAME PROBLEM! 
  //var converter = new System.Drawing.ImageConverter();
  // return (Image)converter.ConvertFrom(byteArrayIn); ;

  using(var memoryStream = new MemoryStream(byteArrayIn)) {
   return Image.FromStream(memoryStream, false, false);
  }
 }

Now, my image data being sent from the server is fine and dandy... I have validated it. This all works fine when I use JSON, too. I've tried many ways to convert from a byte array to an Image, but I always seem to get the Parameter is not valid exception. I've also tried sending my image in different formats like JPG and PNG, as well as raw pixel data.

Anyone have an idea?

Upvotes: 0

Views: 570

Answers (1)

pookie
pookie

Reputation: 4142

Figured it out.

It turns out that the data is backwards...due to FlatBuffers serialization. The solution is to reverse the order of my for-loop during serialization:

for (var i = message.ImageBytes.Length; i -->0;)
{
    builder.AddByte(message.ImageBytes[i]);
}

Upvotes: 0

Related Questions