Я хочу отправить изображения по UDP из сценария Python в Unreal, загрузить их туда и на следующем этапе создать из них текстуру. В настоящее время я изо всех сил пытаюсь получить изображение.
Вот мой простой udp-скрипт на Python:
import cv2, imutils, socket
import numpy as np
import time
import base64
BUFF_SIZE = 65536
server_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_RCVBUF,BUFF_SIZE)
host_name = socket.gethostname()
host_ip = '127.0.0.1'# socket.gethostbyname(host_name)
print(host_ip)
port = 54001
socket_address = (host_ip,port)
server_socket.bind(socket_address)
print('Listening at:',socket_address)
vid = cv2.VideoCapture(0) # replace 'rocket.mp4' with 0 for webcam
fps,st,frames_to_count,cnt = (0,0,20,0)
while True:
msg,client_addr = server_socket.recvfrom(BUFF_SIZE)
print('GOT connection from ',client_addr)
WIDTH=400
while(vid.isOpened()):
_,frame = vid.read()
frame = imutils.resize(frame,width=WIDTH)
encoded,buffer = cv2.imencode('.jpg',frame,[cv2.IMWRITE_JPEG_QUALITY,80])
#message = base64.b64encode(buffer)
server_socket.sendto(bytes(buffer),client_addr)
frame = cv2.putText(frame,'FPS: '+str(fps),(10,40),cv2.FONT_HERSHEY_SIMPLEX,0.7,(0,0,255),2)
cv2.imshow('TRANSMITTING VIDEO',frame)
key = cv2.waitKey(1) & 0xFF
if key == ord('q'):
server_socket.close()
break
if cnt == frames_to_count:
try:
fps = round(frames_to_count/(time.time()-st))
st=time.time()
cnt=0
except:
pass
cnt+=1
Поэтому я отправляю его как массив base64. Как мне загрузить его в Unreal? Я создал простой файл udp .cpp и .h со всем необходимым для запуска UDP-сокета:
void Audp_module::BeginPlay()
{
Super::BeginPlay();
SocketSubsystem = nullptr;
if (SocketSubsystem == nullptr) SocketSubsystem = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM);
SendSize = 2 * 1024 * 1024;
BufferSize = 2 * 1024 * 1024;
FIPv4Address iPAdress;
FIPv4Address::Parse("127.0.0.1",iPAdress);
LocalEndpoint = FIPv4Endpoint(iPAdress, LocalPort);
FIPv4Address::Parse(IP, RemoteAddress);
RemoteEndpoint = FIPv4Endpoint(RemoteAddress, RemotePort);
Socket = nullptr;
if (SocketSubsystem != nullptr)
{
if (Socket == nullptr)
{
Socket = FUdpSocketBuilder(SocketDescription)
.AsNonBlocking()
.AsReusable()
.BoundToEndpoint(LocalEndpoint)
.WithReceiveBufferSize(SendSize)
.WithReceiveBufferSize(BufferSize)
.WithBroadcast();
}
}
}
void Audp_module::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
Super::EndPlay(EndPlayReason);
SocketSubsystem->DestroySocket(Socket);
Socket = nullptr;
SocketSubsystem = nullptr;
}
void Audp_module::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
FString t = "Hello";
sendMessage(t); // Send Message Test
Listen(); // Listen for messages
}
void Audp_module::Listen()
{
TSharedRef<FInternetAddr> targetAddr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
uint32 Size;
if (Socket != nullptr)
{
while (Socket->HasPendingData(Size))
{
uint8* Recv = new uint8[Size];
int32 BytesRead = 0;
ReceivedData.SetNumUninitialized(FMath::Min(Size, 65507u));
Recv = ReceivedData.GetData();
Socket->RecvFrom(ReceivedData.GetData(), ReceivedData.Num(), BytesRead, *targetAddr);
char ansiiData[1024];
memcpy(ansiiData, ReceivedData.GetData(), BytesRead);
ansiiData[BytesRead] = 0;
FString data = ANSI_TO_TCHAR(ansiiData);
//FString data = FString::FromInt(BytesRead);
const auto encoded = FBase64::Encode(*data);
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, encoded);
UE_LOG(LogTemp, Verbose, TEXT("Received: %s"), *encoded);
//FString data = ANSI_TO_TCHAR(ansiiData);
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, "Message by UDP: " + data);
}
}
else
{
Socket = FUdpSocketBuilder(SocketDescription)
.AsNonBlocking()
.AsReusable()
.BoundToEndpoint(LocalEndpoint)
.WithReceiveBufferSize(SendSize)
.WithReceiveBufferSize(BufferSize)
.WithBroadcast();
}
}
Часть после ansiiData не работает, и я предполагаю, что мне нужно выполнить преобразование полученных данных в base64 по-другому? Мой план был сделать что-то вроде описанного здесь
Как я могу это сделать?
Примечание: не выделяйте хранилище, пока не поймете, что оно вам нужно. uint8* Recv = new uint8[Size];
выделяет массив, который теряется, когда Recv = ReceivedData.GetData();
указывает Recv
на другое место. Похоже, что Recv после этого больше никогда не используется, поэтому, скорее всего, вы можете полностью удалить его.
UDP имеет максимальный размер полезной нагрузки 65507 и максимальный практический размер 534. Вероятно, это не будет работать, что бы вы ни исправили.
Возможно ли, что ansiiData слишком мал? Может быть, подогнать его по размеру вместо жесткого кодирования.
char ansiiData[BytesRead + 1];
memcpy(ansiiData, ReceivedData.GetData(), BytesRead);
ansiiData[BytesRead] = 0;
В этом и была проблема, спасибо! Теперь мне нужно найти способ преобразовать его обратно в изображение, а затем в текстуру в Unreal :)
К вашему сведению: UDP имеет максимальный размер пакета ~ 64 КБ. Также нет гарантии заказа и доставки. Также нет необходимости кодировать b64… буфер можно было бы отправить напрямую, если бы он не был слишком большим.