Created
June 17, 2020 18:40
-
-
Save UCIS/c52757fef62239d502806f53b6700a58 to your computer and use it in GitHub Desktop.
Opus in Ogg packetizer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class OggPacketizer : IPacketizer { | |
static UInt32[] checksumTable; | |
static UInt32 serial_counter = 1; | |
uint pageIndex = 0; | |
uint serial; | |
UInt64 position = 0; | |
static OggPacketizer() { | |
initChecksumTable(); | |
} | |
public OggPacketizer() { | |
serial = serial_counter++; | |
} | |
public Byte[] BuildHeader(Byte channels, UInt32 originalSampleRate) { | |
Byte[] id_header = new Byte[19]; | |
setUint32(id_header, 0, 1937076303); // Magic Signature 'Opus' | |
setUint32(id_header, 4, 1684104520); // Magic Signature 'Head' | |
setUint8(id_header, 8, 1); // Version | |
setUint8(id_header, 9, channels); // Channel count | |
setUint16(id_header, 10, 0); // pre-skip, don't need to skip any value | |
setUint32(id_header, 12, originalSampleRate); // original sample rate, any valid sample e.g 8000 | |
setUint16(id_header, 16, 0); // output gain | |
setUint8(id_header, 18, 0); // channel map 0 = one stream: mono or stereo | |
id_header = getPage(id_header, 2, 0, 0); // headerType of ID header is 2 i.e beginning of stream | |
Byte[] comment_header = new Byte[20]; | |
setUint32(comment_header, 0, 1937076303); // Magic Signature 'Opus' | |
setUint32(comment_header, 4, 1936154964); // Magic Signature 'Tags' | |
setUint32(comment_header, 8, 4); // Vendor Length | |
setUint32(comment_header, 12, 1633837924); // Vendor name 'abcd' | |
setUint32(comment_header, 16, 0); // User Comment List Length | |
comment_header = getPage(comment_header, 0, 0, 0); // headerType of comment header is 0 | |
return ArrayUtil.Merge(id_header, comment_header); | |
} | |
private Byte[] getPage(Byte[] segmentData, Byte headerType, UInt32 page_index, UInt64 position) { | |
/* ref: https://tools.ietf.org/id/draft-ietf-codec-oggopus-00.html */ | |
var segmentTable = new Byte[1]; /* segment table stores segment length map. always providing one single segment */ | |
segmentTable = new Byte[segmentData.Length / 255 + 1]; | |
var page = new Byte[27 + segmentTable.Length + segmentData.Length]; | |
//segmentTable[0] = checked((Byte)segmentData.Length); | |
for (int i = 0; i < segmentTable.Length - 1; i++) segmentTable[i] = 255; | |
segmentTable[segmentTable.Length - 1] = (Byte)(segmentData.Length % 255); | |
setUint32(page, 0, 1399285583); // page headers starts with 'OggS' | |
setUint8(page, 4, 0); // Version | |
setUint8(page, 5, headerType); // 1 = continuation, 2 = beginning of stream, 4 = end of stream | |
setUint32(page, 6, (UInt32)position); // granuale position -1 i.e single packet per page. storing into bytes. | |
setUint32(page, 10, (UInt32)(position >> 32)); | |
setUint32(page, 14, serial); // Bitstream serial number | |
setUint32(page, 18, page_index); // Page sequence number | |
setUint8(page, 26, (Byte)segmentTable.Length); // Number of segments in page, giving always 1 segment | |
segmentTable.CopyTo(page, 27); // Segment Table inserting at 27th position since page header length is 27 | |
segmentData.CopyTo(page, 27 + segmentTable.Length); // inserting at 28th since Segment Table(1) + header length(27) | |
setUint32(page, 22, getChecksum(page)); // Checksum - generating for page data and inserting at 22th position into 32 bits | |
return page; | |
} | |
public Byte[] GetDataPage(Byte[] packet, UInt32 samples) { | |
position += samples; | |
return getPage(packet, 0, pageIndex++, position); | |
} | |
private static UInt32 getChecksum(Byte[] data) { | |
UInt32 checksum = 0; | |
for (int i = 0; i < data.Length; i++) { | |
checksum = (checksum << 8) ^ checksumTable[((checksum >> 24) & 0xff) ^ data[i]]; | |
} | |
return checksum >> 0; | |
} | |
private static void initChecksumTable() { | |
checksumTable = new UInt32[256]; | |
for (var i = 0; i < 256; i++) { | |
UInt32 r = (UInt32)(i << 24); | |
for (var j = 0; j < 8; j++) { | |
r = ((r & 0x80000000) != 0) ? ((r << 1) ^ 0x04c11db7) : (r << 1); | |
} | |
checksumTable[i] = (r & 0xffffffff); | |
} | |
} | |
private static void setUint8(Byte[] buffer, int index, Byte value) { | |
buffer[index] = value; | |
} | |
private static void setUint16(Byte[] buffer, int index, UInt16 value) { | |
buffer[index++] = (Byte)(value & 0xFF); | |
buffer[index++] = (Byte)((value >> 8) & 0xFF); | |
} | |
private static void setUint32(Byte[] buffer, int index, UInt32 value) { | |
buffer[index++] = (Byte)(value & 0xFF); | |
buffer[index++] = (Byte)((value >> 8) & 0xFF); | |
buffer[index++] = (Byte)((value >> 16) & 0xFF); | |
buffer[index++] = (Byte)((value >> 24) & 0xFF); | |
} | |
private static void setUint32(Byte[] buffer, int index, Int32 value) { | |
setUint32(buffer, index, (UInt32)value); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment