If anybody ends up curious about this, I was able to reverse-engineer it.
/Transport command is for downloading a single file over a TCP connection. You can use the returned .mov file from /RecordStopConfirm
as the input for /Transport
.
Before transporting you have to spin up a TCP listener and handle the response data. The format is fileSize:int32, data:tarray{byte}
If you want all of the files, you’ll need to look at what files that the Capture Manager window normally inputs, and request each of these 1 by 1.
class FLiveLinkTcpListener : public FRunnable
{
public:
FLiveLinkTcpListener(int32 InPort);
virtual ~FLiveLinkTcpListener();
void SetFileName(const FString& InFileName);
// FRunnable interface
virtual bool Init() override;
virtual uint32 Run() override;
virtual void Stop() override;
DECLARE_DELEGATE_OneParam(FOnFileTransferComplete, const FString& /*FileName*/);
FOnFileTransferComplete OnFileTransferComplete;
private:
void HandleLiveLinkData(FSocket* ConnectionSocket);
const FIPv4Endpoint& GetLocalEndpoint() const;
FSocket* GetSocket() const;
bool IsActive() const;
FOnTcpListenerConnectionAccepted& OnConnectionAccepted();
bool bDeleteSocket = false;
FIPv4Endpoint Endpoint;
FTimespan SleepTime;
FSocket* Socket = nullptr;
bool bStopping = false;
bool bSocketReusable = true;
FRunnableThread* Thread = nullptr;
FString FileName;
DECLARE_DELEGATE_RetVal_TwoParams(bool, FOnTcpListenerConnectionAccepted, FSocket*, const FIPv4Endpoint&)
FOnTcpListenerConnectionAccepted ConnectionAcceptedDelegate;
};
#include "CaptureOrchestrator/LiveLinkTcpListener.h"
#include "HAL/RunnableThread.h"
#include "Async/Async.h"
FLiveLinkTcpListener::FLiveLinkTcpListener(int32 InPort)
{
FIPv4Address Addr;
FIPv4Address::Parse(TEXT("0.0.0.0"), Addr);
Endpoint = FIPv4Endpoint(Addr, InPort);
Thread = FRunnableThread::Create(this, TEXT("FLiveLinkTcpListener"), 8 * 1024, TPri_Normal);
}
FLiveLinkTcpListener::~FLiveLinkTcpListener()
{
Stop();
if (Thread != nullptr)
{
Thread->Kill(true);
delete Thread;
}
}
void FLiveLinkTcpListener::SetFileName(const FString& InFileName)
{
FileName = InFileName;
}
bool FLiveLinkTcpListener::Init()
{
if (Socket == nullptr)
{
Socket = FTcpSocketBuilder(TEXT("FTcpListener server"))
.AsReusable(bSocketReusable)
.BoundToEndpoint(Endpoint)
.Listening(8)
.WithSendBufferSize(2 * 1024 * 1024);
}
return (Socket != nullptr);
}
uint32 FLiveLinkTcpListener::Run()
{
TSharedRef<FInternetAddr> RemoteAddress = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
while (!bStopping)
{
bool Pending = false;
if (Socket->WaitForPendingConnection(Pending, SleepTime) && Pending)
{
FSocket* ConnectionSocket = Socket->Accept(*RemoteAddress, TEXT("FTcpListener client"));
if (ConnectionSocket != nullptr)
{
// New method for processing incoming data from Live Link
HandleLiveLinkData(ConnectionSocket);
}
}
else if (!Pending && SleepTime == FTimespan::Zero())
{
FPlatformProcess::Sleep(0.f);
}
else
{
FPlatformProcess::Sleep(SleepTime.GetSeconds());
}
}
return 0;
}
void FLiveLinkTcpListener::Stop()
{
bStopping = true;
}
void FLiveLinkTcpListener::HandleLiveLinkData(FSocket* ConnectionSocket)
{
if (!ensure(!FileName.IsEmpty()))
{
return;
}
FString BaseDir = FPaths::ProjectContentDir() / TEXT("Temp") / FPaths::GetPath(FileName);
IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
PlatformFile.CreateDirectoryTree(*BaseDir);
FString FilePath = BaseDir / FPaths::GetCleanFilename(FileName);
IFileHandle* FileHandle = PlatformFile.OpenWrite(*FilePath);
if (!FileHandle)
{
return;
}
int32 TotalBytesRead = 0;
int32 BytesRead = 0;
int32 FileSize = 0;
ConnectionSocket->Recv((uint8*)&FileSize, sizeof(int32), BytesRead);
FileSize = ByteSwap(FileSize);
if (FileSize > 0)
{
TArray<uint8> DataBuffer;
const int32 ChunkSize = 8192;
DataBuffer.SetNumUninitialized(ChunkSize);
while (TotalBytesRead < FileSize)
{
int32 Remaining = FMath::Min(ChunkSize, FileSize - TotalBytesRead);
if (ConnectionSocket->Recv(DataBuffer.GetData(), Remaining, BytesRead))
{
FileHandle->Write(DataBuffer.GetData(), BytesRead);
TotalBytesRead += BytesRead;
}
else
{
break;
}
}
}
delete FileHandle;
if (OnFileTransferComplete.IsBound())
{
OnFileTransferComplete.Execute(FileName);
}
}
const FIPv4Endpoint& FLiveLinkTcpListener::GetLocalEndpoint() const
{
return Endpoint;
}
FSocket* FLiveLinkTcpListener::GetSocket() const
{
return Socket;
}
bool FLiveLinkTcpListener::IsActive() const
{
return ((Socket != nullptr) && !bStopping);
}
FOnTcpListenerConnectionAccepted& FLiveLinkTcpListener::OnConnectionAccepted()
{
return ConnectionAcceptedDelegate;
}