Lumenarium/foldhaus_sacn.h

607 lines
18 KiB
C

#define NETWORKINTID_INVALID -1
#define DEFAULT_STREAMING_ACN_PORT 5568
#define IP_ADDRESS_BYTES 16
#define STARTCODE_DMX 0
/*
* a description of the address space being used
*/
#define PREAMBLE_SIZE_ADDR 0
#define POSTAMBLE_SIZE_ADDR 2
#define ACN_IDENTIFIER_ADDR 4
#define ROOT_FLAGS_AND_LENGTH_ADDR 16
#define ROOT_VECTOR_ADDR 18
#define CID_ADDR 22
#define FRAMING_FLAGS_AND_LENGTH_ADDR 38
#define FRAMING_VECTOR_ADDR 40
#define SOURCE_NAME_ADDR 44
#define PRIORITY_ADDR 108
#define RESERVED_ADDR 109
#define SEQ_NUM_ADDR 111
#define OPTIONS_ADDR 112
#define UNIVERSE_ADDR 113
#define DMP_FLAGS_AND_LENGTH_ADDR 115
#define DMP_VECTOR_ADDR 117
#define DMP_ADDRESS_AND_DATA_ADDR 118
#define FIRST_PROPERTY_ADDRESS_ADDR 119
#define ADDRESS_INC_ADDR 121
#define PROP_COUNT_ADDR 123
#define START_CODE_ADDR 125
#define PROP_VALUES_ADDR (START_CODE_ADDR + 1)
/*
* common sizes
*/
#define STREAM_HEADER_SIZE 126
#define STREAM_BODY_SIZE 512
#define SOURCE_NAME_SIZE 64
#define RLP_PREAMBLE_SIZE 16
#define RLP_POSTAMBLE_SIZE 0
#define ACN_IDENTIFIER_SIZE 12
/*
* data definitions
*/
#define ACN_IDENTIFIER "ASC-E1.17\0\0\0"
#define ROOT_VECTOR 4
#define FRAMING_VECTOR 2
#define DMP_VECTOR 2
#define ADDRESS_AND_DATA_FORMAT 0xa1
#define ADDRESS_INC 1
#define DMP_FIRST_PROPERTY_ADDRESS_FORCE 0
#define RESERVED_VALUE 0
//for support of the early draft
#define DRAFT_STREAM_HEADER_SIZE 90
#define DRAFT_SOURCE_NAME_SIZE 32
//for support of the early draft
#define DRAFT_ROOT_VECTOR 3
const u32 VHD_MAXFLAGBYTES = 7; //The maximum amount of bytes used to pack the flags, len, and vector
const u32 VHD_MAXLEN = 0x0fffff; //The maximum packet length is 20 bytes long
const u32 VHD_MAXMINLENGTH = 4095; //The highest length that will fit in the "smallest" length pack
//Defines for the VHD flags
const u8 VHD_L_FLAG = 0x80;
const u8 VHD_V_FLAG = 0x40;
const u8 VHD_H_FLAG = 0x20;
const u8 VHD_D_FLAG = 0x10;
#define CID_Bytes 16
struct cid
{
u8 Bytes[CID_Bytes];
};
struct sacn_universe
{
s16 Universe;
u8* StartPositionInSendBuffer;
s32 SizeInSendBuffer;
s32 OffsetInSendBuffer;
s32 BeginPixelCopyFromOffset;
platform_network_address_handle SendAddress;
};
struct sacn_send_buffer
{
u8* Memory;
s32 Size;
sacn_send_buffer* Next;
};
struct sacn_universe_buffer
{
sacn_universe* Universes;
s32 Used;
s32 Max;
sacn_universe_buffer* Next;
};
struct streaming_acn
{
memory_arena Memory;
// These get created and freed together
sacn_universe_buffer* UniverseBuffer;
sacn_send_buffer* SendBuffer;
platform_socket_handle SendSocket;
cid CID;
s32 SequenceIterator;
};
// SACN Data Header Functions
internal void InitStreamHeader (u8* Buffer, s32 BufferSize, u16 SlotCount, u8 StartCode, u16 Universe, u8 Priority, u16 Reserved, u8 Options, const char* SourceName, cid CID);
internal void SetStreamHeaderSequence_ (u8* Buffer, u8 Sequence, b32 Draft);
internal void VHD_PackFlags_(u8* Buffer, b32 InheritVec, b32 InheritHead, b32 InheritData);
internal u8* VHD_PackLength_(u8* Buffer, u32 Length, b32 IncludeLength);
internal cid StringToCID_ (const char* String);
#define CalculateSendBufferSize(UniverseCount) ((UniverseCount * (STREAM_HEADER_SIZE + STREAM_BODY_SIZE)) + sizeof(sacn_send_buffer))
#define CalculateUniverseBufferSize(UniverseCount) ((UniverseCount * sizeof(sacn_universe)) + sizeof(sacn_universe_buffer))
// Utility
struct sacn_pixel
{
u8 R;
u8 G;
u8 B;
};
//
internal sacn_universe*
SACNGetUniverse (s32 UniverseNumber, streaming_acn* SACN)
{
sacn_universe* Result = 0;
sacn_universe_buffer* Header = SACN->UniverseBuffer;
while (Header)
{
sacn_universe* Cursor = Header->Universes;
for (s32 i = 0; i < Header->Used; i++)
{
if (Cursor->Universe == UniverseNumber)
{
Result = Cursor;
break;
}
Cursor++;
}
Header = Header->Next;
}
return Result;
}
internal void
SACNPushSendBufferOnList (sacn_send_buffer* ListHead, sacn_send_buffer* NewBuffer)
{
if (ListHead->Next)
{
SACNPushSendBufferOnList(ListHead->Next, NewBuffer);
}
else
{
ListHead->Next = NewBuffer;
}
}
internal sacn_send_buffer*
SACNRemoveSendBufferFromList (sacn_send_buffer* List, sacn_send_buffer* Entry)
{
sacn_send_buffer* ListHead = 0;
if (List != Entry && List->Next)
{
ListHead = SACNRemoveSendBufferFromList(List->Next, Entry);
}
else if (List == Entry)
{
ListHead = Entry->Next;
}
else
{
// NOTE(Peter): Trying to remove an entry from a list that doesn't contain it
InvalidCodePath;
}
return ListHead;
}
internal void
SACNPushUniverseBufferOnList (sacn_universe_buffer* ListHead, sacn_universe_buffer* NewBuffer)
{
if (ListHead->Next)
{
SACNPushUniverseBufferOnList(ListHead->Next, NewBuffer);
}
else
{
ListHead->Next = NewBuffer;
}
}
internal sacn_universe_buffer*
SACNRemoveUniverseBufferFromList (sacn_universe_buffer* List, sacn_universe_buffer* Entry)
{
sacn_universe_buffer* ListHead = 0;
if (List != Entry && List->Next)
{
ListHead = SACNRemoveUniverseBufferFromList(List->Next, Entry);
}
else if (List == Entry)
{
ListHead = Entry->Next;
}
else
{
// NOTE(Peter): Trying to remove an entry from a list that doesn't contain it
InvalidCodePath;
}
return ListHead;
}
struct sacn_add_universes_result
{
sacn_send_buffer* NewSendBuffer;
sacn_universe_buffer* NewUniverseBuffer;
};
internal sacn_add_universes_result
SACNAddUniverses(s32* Universes, s32 UniversesLength, streaming_acn* SACN, context Context)
{
sacn_add_universes_result Result = {};
// Determine which universes are already registered and not to be readded.
// NOTE(Peter): This might create funky behaviour if two sculptures start sending data to the same universe
// but I'm not sure its incorrect behavior. I think, eventually, we will want to spit out a report from
// this function that says what universes were duplicated. We might want to display this information to the user
// in a way that they don't have to exit out of every single time they load the software. Not sure
s32 UniversesToAdd = 0;
for (s32 i = 0; i < UniversesLength; i++)
{
sacn_universe* UniverseExists = SACNGetUniverse(Universes[i], SACN);
if (UniverseExists)
{
Universes[i] = -1;
}
else
{
UniversesToAdd++;
}
}
// Push On New Send and Universe Buffers
s32 SendBufferSize = CalculateSendBufferSize(UniversesToAdd);
u8* SendBufferMemory = PushArray(&SACN->Memory, u8, SendBufferSize);
sacn_send_buffer* SendBufferHeader = (sacn_send_buffer*)SendBufferMemory;
SendBufferHeader->Memory = (u8*)(SendBufferHeader + 1);
SendBufferHeader->Size = SendBufferSize - sizeof(sacn_send_buffer);
SendBufferHeader->Next = 0;
if (SACN->SendBuffer)
{
SACNPushSendBufferOnList(SACN->SendBuffer, SendBufferHeader);
}
else
{
SACN->SendBuffer = SendBufferHeader;
}
s32 UniverseBufferSize = CalculateUniverseBufferSize(UniversesToAdd);
u8* UniverseBufferMemory = PushArray(&SACN->Memory, u8, UniverseBufferSize);
sacn_universe_buffer* UniverseBufferHeader = (sacn_universe_buffer*)UniverseBufferMemory;
UniverseBufferHeader->Universes = (sacn_universe*)(UniverseBufferHeader + 1);
UniverseBufferHeader->Used = 0;
UniverseBufferHeader->Max = UniversesToAdd;
if (SACN->UniverseBuffer)
{
SACNPushUniverseBufferOnList(SACN->UniverseBuffer, UniverseBufferHeader);
}
else
{
SACN->UniverseBuffer = UniverseBufferHeader;
}
// Add each of the valid universes
for (s32 j = 0; j < UniversesLength; j++)
{
if (Universes[j] >= 0)
{
Assert(UniverseBufferHeader->Used < UniverseBufferHeader->Max);
s32 Index = UniverseBufferHeader->Used++;
s32 UniverseID = Universes[j];
UniverseBufferHeader->Universes[Index].Universe = UniverseID;
UniverseBufferHeader->Universes[Index].SizeInSendBuffer = STREAM_HEADER_SIZE + STREAM_BODY_SIZE;
UniverseBufferHeader->Universes[Index].BeginPixelCopyFromOffset = -1;
// Configure how the universe looks into the pixel color buffer
s32 SendBufferOffset = (Index * (STREAM_HEADER_SIZE + STREAM_BODY_SIZE));
u8* SendBufferStartPosition = SendBufferHeader->Memory + SendBufferOffset;
UniverseBufferHeader->Universes[Index].OffsetInSendBuffer = SendBufferOffset;
UniverseBufferHeader->Universes[Index].StartPositionInSendBuffer = SendBufferStartPosition;
// Set up the Send Address
u8 MulticastAddressBuffer[IP_ADDRESS_BYTES];
GSMemSet(MulticastAddressBuffer, 0, IP_ADDRESS_BYTES);
MulticastAddressBuffer[12] = 239;
MulticastAddressBuffer[13] = 255;
PackB2(MulticastAddressBuffer + 14, UniverseID);
u_long V4Address = (u_long)UpackB4(MulticastAddressBuffer + IP_ADDRESS_BYTES - sizeof(u32));
GSMemSet(&UniverseBufferHeader->Universes[Index].SendAddress, 0, sizeof(sockaddr_in));
UniverseBufferHeader->Universes[Index].SendAddress = Context.PlatformGetSendAddress(
AF_INET,
HostToNetU16(DEFAULT_STREAMING_ACN_PORT),
HostToNetU32(V4Address));
#if 0 // Old Net Code
UniverseBufferHeader->Universes[Index].SendAddress.sin_family = AF_INET;
UniverseBufferHeader->Universes[Index].SendAddress.sin_port = HostToNetU16(DEFAULT_STREAMING_ACN_PORT);
UniverseBufferHeader->Universes[Index].SendAddress.sin_addr.s_addr = HostToNetU32(V4Address);
#endif
s32 SlotCount = 512;
InitStreamHeader(UniverseBufferHeader->Universes[Index].StartPositionInSendBuffer,
UniverseBufferHeader->Universes[Index].SizeInSendBuffer,
SlotCount,
STARTCODE_DMX,
UniverseID,
0,
0, // Reserved
0, // Options
"Source 1",
SACN->CID
);
}
}
Result.NewUniverseBuffer = UniverseBufferHeader;
Result.NewSendBuffer= SendBufferHeader;
return Result;
}
internal void
SACNRemoveUniverseAndSendBuffer(streaming_acn* SACN, sacn_universe_buffer* Universes, sacn_send_buffer* SendBuffer)
{
SACN->UniverseBuffer = SACNRemoveUniverseBufferFromList(SACN->UniverseBuffer, Universes);
SACN->SendBuffer = SACNRemoveSendBufferFromList(SACN->SendBuffer, SendBuffer);
}
internal streaming_acn
InitializeSACN (platform_alloc* PlatformAlloc, context Context)
{
streaming_acn SACN = {};
InitMemoryArena(&SACN.Memory, 0, 0, PlatformAlloc);
SACN.SendSocket = Context.PlatformGetSocketHandle(AF_INET, SOCK_DGRAM, 0);
int Multicast_TimeToLive = 20;
int Error = Context.PlatformSetSocketOption(SACN.SendSocket, IPPROTO_IP, IP_MULTICAST_TTL,
(const char*)(&Multicast_TimeToLive), sizeof(Multicast_TimeToLive));
SACN.CID = StringToCID_ ("{67F9D986-544E-4abb-8986-D5F79382586C}");
SACN.UniverseBuffer = 0;
SACN.SendBuffer = 0;
return SACN;
}
internal void
SACNSendDataToUniverse (streaming_acn* SACN, sacn_universe* Universe, platform_send_to* PlatformSendTo)
{
//DEBUG_TRACK_FUNCTION;
u8* StartPositionInSendBuffer = (u8*)Universe->StartPositionInSendBuffer;
SetStreamHeaderSequence_(StartPositionInSendBuffer, SACN->SequenceIterator, false);
PlatformSendTo(SACN->SendSocket, Universe->SendAddress, (const char*)StartPositionInSendBuffer, Universe->SizeInSendBuffer, 0);
#if 0 // Old Network Code
// TODO(Peter): HUGE NOTE!!!!!!!!
// This needs to be put on a separate thread. The sendto call is really slowing us down.
s32 LengthSent = sendto(SACN->SendSocket, (const char*)StartPositionInSendBuffer, Universe->SizeInSendBuffer,
0, (sockaddr*)(&Universe->SendAddress), sizeof(sockaddr_in));
if (LengthSent == SOCKET_ERROR)
{
s32 LastSocketError = WSAGetLastError();
InvalidCodePath;
}
#endif
}
internal void
SACNCleanup(streaming_acn* SACN, context Context)
{
Context.PlatformCloseSocket(SACN->SendSocket);
}
///////////////////////////////////////////////
//
// SACN Data Header Functions
//
///////////////////////////////////////////////
internal void
InitStreamHeader (u8* Buffer, s32 BufferSize,
u16 SlotCount,
u8 StartCode,
u16 Universe,
u8 Priority,
u16 Reserved,
u8 Options,
const char* SourceName,
cid CID
)
{
u8* Cursor = Buffer;
// Preamble Size
Cursor = PackB2(Cursor, RLP_PREAMBLE_SIZE);
Cursor = PackB2(Cursor, RLP_POSTAMBLE_SIZE);
memcpy(Cursor, ACN_IDENTIFIER, ACN_IDENTIFIER_SIZE);
Cursor += ACN_IDENTIFIER_SIZE;
// TODO(Peter): If you never use this anywhere else, go back and remove the parameters
VHD_PackFlags_(Cursor, false, false, false);
Cursor = VHD_PackLength_(Cursor,
STREAM_HEADER_SIZE - RLP_PREAMBLE_SIZE + SlotCount,
false);
// root vector
Cursor = PackB4(Cursor, ROOT_VECTOR);
// CID Pack
for (s32 i = 0; i < CID_Bytes; i++)
{
*Cursor++ = CID.Bytes[i];
}
VHD_PackFlags_(Cursor, false, false, false);
Cursor = VHD_PackLength_(Cursor,
STREAM_HEADER_SIZE - FRAMING_FLAGS_AND_LENGTH_ADDR + SlotCount,
false);
// framing vector
Cursor = PackB4(Cursor, FRAMING_VECTOR);
// framing source name
strncpy((char*)Cursor, SourceName, SOURCE_NAME_SIZE);
Cursor[SOURCE_NAME_SIZE - 1] = '\0';
Cursor += SOURCE_NAME_SIZE;
// priority
Cursor = PackB1(Cursor, Priority);
// reserved
Cursor = PackB2(Cursor, Reserved);
// Sequence # is always set to 0/NONE at the beginning, but it is incremented when sending data
Cursor = PackB1(Cursor, 0);
// Options
Cursor = PackB1(Cursor, Options);
// Universe
Cursor = PackB2(Cursor, Universe);
VHD_PackFlags_(Cursor, false, false, false);
Cursor = VHD_PackLength_(Cursor,
STREAM_HEADER_SIZE - DMP_FLAGS_AND_LENGTH_ADDR + SlotCount,
false);
// DMP Vector
Cursor = PackB1(Cursor, DMP_VECTOR);
// DMP Address and data type
Cursor = PackB1(Cursor, ADDRESS_AND_DATA_FORMAT);
// DMP first property address
Cursor = PackB2(Cursor, 0);
// DMP Address Increment
Cursor = PackB2(Cursor, ADDRESS_INC);
// Property Value Count -- Includes one byte for start code
Cursor = PackB2(Cursor, SlotCount + 1);
Cursor = PackB1(Cursor, StartCode);
s32 DiffSize = Cursor - Buffer;
if (Cursor - Buffer != STREAM_HEADER_SIZE)
{
InvalidCodePath;
}
}
internal void
SetStreamHeaderSequence_ (u8* Buffer, u8 Sequence, b32 Draft)
{
DEBUG_TRACK_FUNCTION;
PackB1(Buffer + SEQ_NUM_ADDR, Sequence);
}
internal void
VHD_PackFlags_(u8* Buffer, b32 InheritVec, b32 InheritHead, b32 InheritData)
{
u8* Cursor = Buffer;
u8 NewByte = UpackB1(Cursor) & 0x8f;
if (!InheritVec) { NewByte |= VHD_V_FLAG; }
if (!InheritHead) { NewByte |= VHD_H_FLAG; }
if (!InheritData) { NewByte |= VHD_D_FLAG; }
PackB1(Cursor, NewByte);
}
internal u8*
VHD_PackLength_(u8* Buffer, u32 Length, b32 IncludeLength)
{
u8* Cursor = Buffer;
u32 AdjustedLength = Length;
if (IncludeLength)
{
if (Length + 1 > VHD_MAXMINLENGTH)
{
AdjustedLength += 2;
}
else
{
AdjustedLength += 1;
}
}
// Mask out the length bits to keep flags intact
u8 NewByte = UpackB1(Cursor) & 0x70;
if (AdjustedLength > VHD_MAXMINLENGTH)
{
NewByte |= VHD_L_FLAG;
}
u8 PackBuffer[4];
PackB4(PackBuffer, AdjustedLength);
if (AdjustedLength <= VHD_MAXMINLENGTH)
{
NewByte |= (PackBuffer[2] & 0x0f);
Cursor = PackB1(Cursor, NewByte);
Cursor = PackB1(Cursor, PackBuffer[3]);
}
else
{
NewByte |= (PackBuffer[1] & 0x0f);
Cursor = PackB1(Cursor, PackBuffer[2]);
Cursor = PackB1(Cursor, PackBuffer[3]);
}
return Cursor;
}
internal cid
StringToCID_ (const char* String)
{
cid Result = {};
const char* Src = String;
u8* Dest = &Result.Bytes[0];
b32 FirstNibble = true;
while(*Src && (Dest - &Result.Bytes[0] < CID_Bytes))
{
u8 Offset = 0;
if ((*Src >= 0x30) && (*Src <= 0x39)){ Offset = 0x30; }
else if ((*Src >= 0x41) && (*Src <= 0x46)) { Offset = 0x37; }
else if ((*Src >= 0x61) && (*Src <= 0x66)) { Offset = 0x66; }
if (Offset != 0)
{
if (FirstNibble)
{
*Dest = (u8)(*Src - Offset);
*Dest <<= 4;
FirstNibble = false;
}
else
{
*Dest |= (*Src - Offset);
Dest++;
FirstNibble = true;
}
}
Src++;
}
return Result;
}