348 lines
8.6 KiB
C
348 lines
8.6 KiB
C
//
|
|
// File: sacn.h
|
|
// Author: Peter Slattery
|
|
// Creation Date: 2020-01-01
|
|
//
|
|
#ifndef SACN_H
|
|
|
|
#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 streaming_acn
|
|
{
|
|
platform_socket_handle SendSocket;
|
|
cid CID;
|
|
s32 SequenceIterator;
|
|
};
|
|
|
|
///////////////////////////////////////////////
|
|
//
|
|
// SACN Data Header Functions
|
|
//
|
|
///////////////////////////////////////////////
|
|
|
|
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;
|
|
}
|
|
|
|
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);
|
|
|
|
GSMemCopy(ACN_IDENTIFIER, Cursor, 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
|
|
// :Check
|
|
GSMemCopy(SourceName, (char*)Cursor, 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);
|
|
|
|
Assert(Cursor - Buffer == STREAM_HEADER_SIZE);
|
|
}
|
|
|
|
//
|
|
// New SACN
|
|
//
|
|
|
|
internal streaming_acn
|
|
InitializeSACN ( context Context)
|
|
{
|
|
streaming_acn SACN = {};
|
|
|
|
s32 Multicast_TimeToLive = 20;
|
|
SACN.SendSocket = Context.PlatformGetSocketHandle(Multicast_TimeToLive);
|
|
SACN.CID = StringToCID_ ("{67F9D986-544E-4abb-8986-D5F79382586C}");
|
|
|
|
return SACN;
|
|
}
|
|
|
|
internal void
|
|
SACNCleanup(streaming_acn* SACN, context Context)
|
|
{
|
|
Context.PlatformCloseSocket(SACN->SendSocket);
|
|
}
|
|
|
|
internal void
|
|
SACNUpdateSequence (streaming_acn* SACN)
|
|
{
|
|
// Never use 0 after the first one
|
|
if (++SACN->SequenceIterator == 0)
|
|
{
|
|
++SACN->SequenceIterator;
|
|
}
|
|
}
|
|
|
|
internal void
|
|
SACNPrepareBufferHeader (s32 Universe, u8* Buffer, s32 BufferSize, s32 SizeReservedForHeader, streaming_acn SACN)
|
|
{
|
|
Assert(SizeReservedForHeader == STREAM_HEADER_SIZE);
|
|
Assert(Buffer && BufferSize > 0);
|
|
|
|
s32 Priority = 0;
|
|
InitStreamHeader(Buffer, BufferSize, STREAM_BODY_SIZE, STARTCODE_DMX, Universe, Priority, 0, 0, "Lumenarium", SACN.CID);
|
|
SetStreamHeaderSequence_(Buffer, SACN.SequenceIterator, false);
|
|
}
|
|
|
|
internal u32
|
|
SACNGetUniverseSendAddress(s32 Universe)
|
|
{
|
|
u8 MulticastAddressBuffer[4] = {};
|
|
MulticastAddressBuffer[0] = 239;
|
|
MulticastAddressBuffer[1] = 255;
|
|
MulticastAddressBuffer[2] = (u8)((Universe & 0xff00) >> 8); // high bit
|
|
MulticastAddressBuffer[3] = (u8)((Universe & 0x00ff)); // low bit
|
|
|
|
u32 V4Address = (u32)UpackB4(MulticastAddressBuffer);
|
|
return V4Address;
|
|
}
|
|
|
|
|
|
#define SACN_H
|
|
#endif // SACN_H
|