unity socket programming c# sockets unity3d streaming

c# - socket - Unidad: transmisión de video en vivo



socket programming c# (1)

Ejecuté su código y funcionó a veces y falló a veces (aproximadamente el 90% del tiempo). Funcionó con en mi computadora con 5 FPS . Esto no funcionará bien en un dispositivo móvil que estoy seguro de que está apuntando a iPad.

Hay pocos problemas en su código, pero son problemas muy serios.

1. Su imagen no se recibe completamente antes de cargarla.

Es por eso que tu imagen se ve tan rara.

El error más grande que comete la gente cuando trabaja con socket es asumir que todo lo que envíe se recibirá de inmediato. Esto no es verdad. Así se codifica su cliente. Por favor lee this .

Este es el método que utilicé en mi respuesta:

A .Get Texture2D byte array.

B. Envíe la longitud de la matriz de bytes. No la matriz de bytes sino la longitud.

C. El cliente leerá primero la longitud.

D. El cliente usará esa longitud para leer todos los datos de textura / píxel hasta su finalización.

E. Convierta los bytes recibidos en matriz.

Puede ver las private int readImageByteSize(int size) y private void readFrameByteArray(int size) para saber cómo leer todos los bytes.

Por supuesto, también debe conocer la longitud de la longitud de los datos que se envían primero. La longitud se guarda en int tipo de datos.

El valor int máximo es 2,147,483,647 y eso es 10 dígitos de largo. Entonces, hice que la longitud de la matriz que se envía primero sea 15 como protocolo. Esta es una regla que también debe obedecerse en el lado del cliente.

Así es como funciona ahora:

Lea la matriz de bytes de Texture2D , lea la longitud de esa matriz y envíela al cliente. El cliente sigue una regla de que los primeros 15 bytes son simplemente la longitud. El cliente leería esos 15 bytes, los volvería a convertir en longitud y luego los usaría en un bucle para leer Texture2D completo del servidor.

La conversión de longitud se realiza con las void byteLengthToFrameByteArray(int byteLength, byte[] fullBytes) e int frameByteArrayToByteLength(byte[] frameBytesLength) . Echa un vistazo a aquellos para entenderlos.

2. Realización de la operación del zócalo en el subproceso principal.

Es por eso que el FPS es 5 en mi computadora.

No haga esto, ya que esto hará que su velocidad de cuadros sea baja tal como ya lo es. He respondido muchas preguntas como esta, pero no profundizaré porque parece que sabes lo que estás haciendo e intenté usar Thread pero lo hice mal.

A. Estaba leyendo desde el Thread principal cuando lo hizo: serverStream.Read(readBuffer, 0, readBuffer.Length); en la función de Update .

Deberías haber hecho eso por dentro

Loom.RunAsync(() => { //your red code });

B. SendPng el mismo error en la función SendPng , cuando estaba enviando datos con la stream.Write(pngBytes, 0, pngBytes.Length); en el

Loom.QueueOnMainThread(() => {});

Todo lo que hagas dentro de Loom.QueueOnMainThread se realizará en el Thread principal.

Se supone que debe realizar el envío en otro Thread.Loom.RunAsync(() =>{});

Finalmente, listner = new TcpListener(port); es obsoluto Esto no causó ningún problema, pero use listner = new TcpListener(IPAddress.Any, port); en el código de su servidor que debería escuchar nay IP.

El FPS final tiene más de 50 en mi computadora después de hacer todas estas correcciones. El siguiente código se puede mejorar mucho. Lo dejaré para que lo hagas.

Puede usar la comparación de código en línea para ver las cosas que cambiaron en cada clase.

SERVIDOR

using UnityEngine; using System.Collections; using System.IO; using UnityEngine.UI; using System; using System.Text; using System.Net; using System.Net.Sockets; using System.Threading; using System.Collections.Generic; public class Connecting : MonoBehaviour { WebCamTexture webCam; public RawImage myImage; public bool enableLog = false; Texture2D currentTexture; private TcpListener listner; private const int port = 8010; private bool stop = false; private List<TcpClient> clients = new List<TcpClient>(); //This must be the-same with SEND_COUNT on the client const int SEND_RECEIVE_COUNT = 15; private void Start() { Application.runInBackground = true; //Start WebCam coroutine StartCoroutine(initAndWaitForWebCamTexture()); } //Converts the data size to byte array and put result to the fullBytes array void byteLengthToFrameByteArray(int byteLength, byte[] fullBytes) { //Clear old data Array.Clear(fullBytes, 0, fullBytes.Length); //Convert int to bytes byte[] bytesToSendCount = BitConverter.GetBytes(byteLength); //Copy result to fullBytes bytesToSendCount.CopyTo(fullBytes, 0); } //Converts the byte array to the data size and returns the result int frameByteArrayToByteLength(byte[] frameBytesLength) { int byteLength = BitConverter.ToInt32(frameBytesLength, 0); return byteLength; } IEnumerator initAndWaitForWebCamTexture() { // Open the Camera on the desired device, in my case IPAD pro webCam = new WebCamTexture(); // Get all devices , front and back camera webCam.deviceName = WebCamTexture.devices[WebCamTexture.devices.Length - 1].name; // request the lowest width and heigh possible webCam.requestedHeight = 10; webCam.requestedWidth = 10; myImage.texture = webCam; webCam.Play(); currentTexture = new Texture2D(webCam.width, webCam.height); // Connect to the server listner = new TcpListener(IPAddress.Any, port); listner.Start(); while (webCam.width < 100) { yield return null; } //Start sending coroutine StartCoroutine(senderCOR()); } WaitForEndOfFrame endOfFrame = new WaitForEndOfFrame(); IEnumerator senderCOR() { bool isConnected = false; TcpClient client = null; NetworkStream stream = null; // Wait for client to connect in another Thread Loom.RunAsync(() => { while (!stop) { // Wait for client connection client = listner.AcceptTcpClient(); // We are connected clients.Add(client); isConnected = true; stream = client.GetStream(); } }); //Wait until client has connected while (!isConnected) { yield return null; } LOG("Connected!"); bool readyToGetFrame = true; byte[] frameBytesLength = new byte[SEND_RECEIVE_COUNT]; while (!stop) { //Wait for End of frame yield return endOfFrame; currentTexture.SetPixels(webCam.GetPixels()); byte[] pngBytes = currentTexture.EncodeToPNG(); //Fill total byte length to send. Result is stored in frameBytesLength byteLengthToFrameByteArray(pngBytes.Length, frameBytesLength); //Set readyToGetFrame false readyToGetFrame = false; Loom.RunAsync(() => { //Send total byte count first stream.Write(frameBytesLength, 0, frameBytesLength.Length); LOG("Sent Image byte Length: " + frameBytesLength.Length); //Send the image bytes stream.Write(pngBytes, 0, pngBytes.Length); LOG("Sending Image byte array data : " + pngBytes.Length); //Sent. Set readyToGetFrame true readyToGetFrame = true; }); //Wait until we are ready to get new frame(Until we are done sending data) while (!readyToGetFrame) { LOG("Waiting To get new frame"); yield return null; } } } void LOG(string messsage) { if (enableLog) Debug.Log(messsage); } private void Update() { myImage.texture = webCam; } // stop everything private void OnApplicationQuit() { if (webCam != null && webCam.isPlaying) { webCam.Stop(); stop = true; } if (listner != null) { listner.Stop(); } foreach (TcpClient c in clients) c.Close(); } }

CLIENTE

using UnityEngine; using System.Collections; using UnityEngine.UI; using System.Net.Sockets; using System.Net; using System.IO; using System; public class reciver : MonoBehaviour { public RawImage image; public bool enableLog = false; const int port = 8010; public string IP = "192.168.1.165"; TcpClient client; Texture2D tex; private bool stop = false; //This must be the-same with SEND_COUNT on the server const int SEND_RECEIVE_COUNT = 15; // Use this for initialization void Start() { Application.runInBackground = true; tex = new Texture2D(0, 0); client = new TcpClient(); //Connect to server from another Thread Loom.RunAsync(() => { LOGWARNING("Connecting to server..."); // if on desktop client.Connect(IPAddress.Loopback, port); // if using the IPAD //client.Connect(IPAddress.Parse(IP), port); LOGWARNING("Connected!"); imageReceiver(); }); } void imageReceiver() { //While loop in another Thread is fine so we don''t block main Unity Thread Loom.RunAsync(() => { while (!stop) { //Read Image Count int imageSize = readImageByteSize(SEND_RECEIVE_COUNT); LOGWARNING("Received Image byte Length: " + imageSize); //Read Image Bytes and Display it readFrameByteArray(imageSize); } }); } //Converts the data size to byte array and put result to the fullBytes array void byteLengthToFrameByteArray(int byteLength, byte[] fullBytes) { //Clear old data Array.Clear(fullBytes, 0, fullBytes.Length); //Convert int to bytes byte[] bytesToSendCount = BitConverter.GetBytes(byteLength); //Copy result to fullBytes bytesToSendCount.CopyTo(fullBytes, 0); } //Converts the byte array to the data size and returns the result int frameByteArrayToByteLength(byte[] frameBytesLength) { int byteLength = BitConverter.ToInt32(frameBytesLength, 0); return byteLength; } /////////////////////////////////////////////////////Read Image SIZE from Server/////////////////////////////////////////////////// private int readImageByteSize(int size) { bool disconnected = false; NetworkStream serverStream = client.GetStream(); byte[] imageBytesCount = new byte[size]; var total = 0; do { var read = serverStream.Read(imageBytesCount, total, size - total); //Debug.LogFormat("Client recieved {0} bytes", total); if (read == 0) { disconnected = true; break; } total += read; } while (total != size); int byteLength; if (disconnected) { byteLength = -1; } else { byteLength = frameByteArrayToByteLength(imageBytesCount); } return byteLength; } /////////////////////////////////////////////////////Read Image Data Byte Array from Server/////////////////////////////////////////////////// private void readFrameByteArray(int size) { bool disconnected = false; NetworkStream serverStream = client.GetStream(); byte[] imageBytes = new byte[size]; var total = 0; do { var read = serverStream.Read(imageBytes, total, size - total); //Debug.LogFormat("Client recieved {0} bytes", total); if (read == 0) { disconnected = true; break; } total += read; } while (total != size); bool readyToReadAgain = false; //Display Image if (!disconnected) { //Display Image on the main Thread Loom.QueueOnMainThread(() => { displayReceivedImage(imageBytes); readyToReadAgain = true; }); } //Wait until old Image is displayed while (!readyToReadAgain) { System.Threading.Thread.Sleep(1); } } void displayReceivedImage(byte[] receivedImageBytes) { tex.LoadImage(receivedImageBytes); image.texture = tex; } // Update is called once per frame void Update() { } void LOG(string messsage) { if (enableLog) Debug.Log(messsage); } void LOGWARNING(string messsage) { if (enableLog) Debug.LogWarning(messsage); } void OnApplicationQuit() { LOGWARNING("OnApplicationQuit"); stop = true; if (client != null) { client.Close(); } } }

Estoy tratando de transmitir un video en vivo de una aplicación a otra. Actualmente tengo 2 aplicaciones. donde la aplicación 1 es el servidor / remitente y la aplicación 2 es el cliente / receptor. En la aplicación 1 envié con éxito los bytes de video al cliente. y en el lado del cliente también recibo todos los bytes. Estoy usando sockets y TCP. El problema al que me enfrento es que, cuando recibo los bytes de video y los asigno a una textura de Imagen sin formato, la imagen en la textura parece demasiado ampliada y está muy pixelada.

Imagen actualizada

Esto es lo que transmito

y esto es lo que obtengo del cliente.

Este es el primer problema, sin embargo, actualmente estoy probando de escritorio a otro, mi objetivo es transmitir un iPad a un escritorio, y cuando lo hago es muy lento y mata la aplicación tanto en el iPad como en el escritorio.

Algunos problemas que probé hasta ahora.

1: Creo que esto está sucediendo porque tengo 2 resoluciones diferentes porque transmito de ipad a Desktop

2: La imagen de textura es demasiado grande, la imprimo y devuelve 630. Intenté cambiar su tamaño usando Unity Texture2D.resize pero obtengo una textura gris porque la función establece los píxeles como no identificados

3: Utilicé otras bibliotecas para cambiar el tamaño de las texturas y obtengo lo que quiero, pero después de 12 cuadros, la imagen en bruto comienza a parpadear entre el video y "? textura tanto que luego se congela en ambas aplicaciones (ipad y escritorio)

4: creo que la forma en que estoy leyendo la textura está causando el problema porque uso las funciones Setpixels y Getpixels y son pesadas.

Mi código: servidor / lado del remitente:

using UnityEngine; using System.Collections; using System.IO; using UnityEngine.UI; using System; using System.Text; using System.Net; using System.Net.Sockets; using System.Threading; using System.Collections.Generic; public class Connecting : MonoBehaviour { WebCamTexture webCam; public RawImage myImage; Texture2D currentTexture; private TcpListener listner; private const int port = 8010; private bool stop = false; private List<TcpClient> clients = new List<TcpClient>(); private void Start() { // Open the Camera on the desired device, in my case IPAD pro webCam = new WebCamTexture(); // Get all devices , front and back camera webCam.deviceName = WebCamTexture.devices[WebCamTexture.devices.Length - 1].name; // request the lowest width and heigh possible webCam.requestedHeight = 10; webCam.requestedWidth = 10; webCam.Play(); / currentTexture = new Texture2D(webCam.width, webCam.height); // Connect to the server listner = new TcpListener(port); listner.Start(); // Create Seperate thread for requesting from client Loom.RunAsync(() => { while (!stop) { // Wait for client approval var client = listner.AcceptTcpClient(); // We are connected clients.Add(client); Loom.RunAsync(() => { while (!stop) { var stremReader = client.GetStream(); if (stremReader.CanRead) { // we need storage for data using (var messageData = new MemoryStream()) { Byte[] buffer = new Byte[client.ReceiveBufferSize]; while (stremReader.DataAvailable) { int bytesRead = stremReader.Read(buffer, 0, buffer.Length); if (bytesRead == 0) break; // Writes to the data storage messageData.Write(buffer, 0, bytesRead); } if (messageData.Length > 0) { // send pngImage SendPng(client); } } } } }); } }); } private void Update() { myImage.texture = webCam; } // Read video pixels and send them to the client private void SendPng (TcpClient client) { Loom.QueueOnMainThread(() => { // Get the webcame texture pixels currentTexture.SetPixels(webCam.GetPixels()); var pngBytes = currentTexture.EncodeToPNG(); // Want to Write var stream = client.GetStream(); // Write the image bytes stream.Write(pngBytes, 0, pngBytes.Length); // send it stream.Flush(); }); } // stop everything private void OnApplicationQuit() { webCam.Stop(); stop = true; listner.Stop(); foreach (TcpClient c in clients) c.Close(); } }

Lado del cliente / receptor

using UnityEngine; using System.Collections; using UnityEngine.UI; using System.Net.Sockets; using System.Net; using System.IO; public class reciver : MonoBehaviour { public RawImage image; const int port = 8010; public string IP = ""; TcpClient client; Texture2D tex; // Use this for initialization void Start() { client = new TcpClient(); // connect to server Loom.RunAsync(() => { Debug.LogWarning("Connecting to server..."); // if on desktop client.Connect(IPAddress.Loopback, port); // if using the IPAD //client.Connect(IPAddress.Parse(IP), port); Debug.LogWarning("Connected!"); }); } float lastTimeRequestedTex = 0; // Update is called once per frame void Update() { //if (Time.time - lastTimeRequestedTex < 0.1f) // return; lastTimeRequestedTex = Time.time; if (!client.Connected) return; // Send 1 byte to server var serverStream = client.GetStream(); // request the texture from the server if (serverStream.CanWrite) { // Texture request // send request serverStream.WriteByte(byte.MaxValue); serverStream.Flush(); Debug.Log("Succesfully send 1 byte"); } if (serverStream.CanRead) { // Read the bytes using (var writer = new MemoryStream()) { var readBuffer = new byte[client.ReceiveBufferSize]; while (serverStream.DataAvailable) { int numberOfBytesRead = serverStream.Read(readBuffer, 0, readBuffer.Length); if (numberOfBytesRead <= 0) { break; } writer.Write(readBuffer, 0, numberOfBytesRead); } if (writer.Length > 0) { // got whole data in writer // Get the bytes and apply them to the texture var tex = new Texture2D(0, 0); tex.LoadImage(writer.ToArray()); Debug.Log(tex.width + tex.height); image.texture = tex; } } } } void OnApplicationQuit() { Debug.LogWarning("OnApplicationQuit"); client.Close(); } }