113 lines
2.5 KiB
TypeScript
113 lines
2.5 KiB
TypeScript
/**
|
|||
|
|
* H264/H265 NAL helpers — ported from @dank074/discord-video-stream
|
||
|
|
* AnnexBHelper.js. Only the H264 parts are used by GoLive (H264 encoder),
|
||
|
|
* H265 constants kept for completeness of the port.
|
||
|
|
*/
|
||
|
|
|
||
|
|
export enum H264NalUnitTypes {
|
||
|
|
Unspecified = 0,
|
||
|
|
CodedSliceNonIDR = 1,
|
||
|
|
CodedSlicePartitionA = 2,
|
||
|
|
CodedSlicePartitionB = 3,
|
||
|
|
CodedSlicePartitionC = 4,
|
||
|
|
CodedSliceIdr = 5,
|
||
|
|
SEI = 6,
|
||
|
|
SPS = 7,
|
||
|
|
PPS = 8,
|
||
|
|
AccessUnitDelimiter = 9,
|
||
|
|
EndOfSequence = 10,
|
||
|
|
EndOfStream = 11,
|
||
|
|
FillerData = 12,
|
||
|
|
SEIExtenstion = 13,
|
||
|
|
PrefixNalUnit = 14,
|
||
|
|
SubsetSPS = 15,
|
||
|
|
}
|
||
|
|
|
||
|
|
export enum H265NalUnitTypes {
|
||
|
|
TRAIL_N = 0,
|
||
|
|
TRAIL_R = 1,
|
||
|
|
TSA_N = 2,
|
||
|
|
TSA_R = 3,
|
||
|
|
STSA_N = 4,
|
||
|
|
STSA_R = 5,
|
||
|
|
RADL_N = 6,
|
||
|
|
RADL_R = 7,
|
||
|
|
RASL_N = 8,
|
||
|
|
RASL_R = 9,
|
||
|
|
RSV_VCL_N10 = 10,
|
||
|
|
RSV_VCL_R11 = 11,
|
||
|
|
RSV_VCL_N12 = 12,
|
||
|
|
RSV_VCL_R13 = 13,
|
||
|
|
RSV_VCL_N14 = 14,
|
||
|
|
RSV_VCL_R15 = 15,
|
||
|
|
BLA_W_LP = 16,
|
||
|
|
BLA_W_RADL = 17,
|
||
|
|
BLA_N_LP = 18,
|
||
|
|
IDR_W_RADL = 19,
|
||
|
|
IDR_N_LP = 20,
|
||
|
|
CRA_NUT = 21,
|
||
|
|
RSV_IRAP_VCL22 = 22,
|
||
|
|
RSV_IRAP_VCL23 = 23,
|
||
|
|
RSV_VCL24 = 24,
|
||
|
|
RSV_VCL25 = 25,
|
||
|
|
RSV_VCL26 = 26,
|
||
|
|
RSV_VCL27 = 27,
|
||
|
|
RSV_VCL28 = 28,
|
||
|
|
RSV_VCL29 = 29,
|
||
|
|
RSV_VCL30 = 30,
|
||
|
|
RSV_VCL31 = 31,
|
||
|
|
VPS_NUT = 32,
|
||
|
|
SPS_NUT = 33,
|
||
|
|
PPS_NUT = 34,
|
||
|
|
AUD_NUT = 35,
|
||
|
|
EOS_NUT = 36,
|
||
|
|
EOB_NUT = 37,
|
||
|
|
FD_NUT = 38,
|
||
|
|
PREFIX_SEI_NUT = 39,
|
||
|
|
SUFFIX_SEI_NUT = 40,
|
||
|
|
}
|
||
|
|
|
||
|
|
export const H264Helpers = {
|
||
|
|
getUnitType(frame: Uint8Array): number {
|
||
|
|
return frame[0] & 0x1f;
|
||
|
|
},
|
||
|
|
splitHeader(frame: Uint8Array): [Uint8Array, Uint8Array] {
|
||
|
|
return [frame.subarray(0, 1), frame.subarray(1)];
|
||
|
|
},
|
||
|
|
isAUD(unitType: number): boolean {
|
||
|
|
return unitType === H264NalUnitTypes.AccessUnitDelimiter;
|
||
|
|
},
|
||
|
|
};
|
||
|
|
|
||
|
|
export const H265Helpers = {
|
||
|
|
getUnitType(frame: Uint8Array): number {
|
||
|
|
return (frame[0] >> 1) & 0x3f;
|
||
|
|
},
|
||
|
|
splitHeader(frame: Uint8Array): [Uint8Array, Uint8Array] {
|
||
|
|
return [frame.subarray(0, 2), frame.subarray(2)];
|
||
|
|
},
|
||
|
|
isAUD(unitType: number): boolean {
|
||
|
|
return unitType === H265NalUnitTypes.AUD_NUT;
|
||
|
|
},
|
||
|
|
};
|
||
|
|
|
||
|
|
export const startCode3 = Buffer.from([0, 0, 1]);
|
||
|
|
|
||
|
|
/** Split an AnnexB bitstream into NAL units (start codes stripped). */
|
||
|
|
export function splitNalu(buf: Buffer): Buffer[] {
|
||
|
|
let temp: Buffer | null = buf;
|
||
|
|
const nalus: Buffer[] = [];
|
||
|
|
while (temp?.byteLength) {
|
||
|
|
let pos: number = temp.indexOf(startCode3);
|
||
|
|
let length = 3;
|
||
|
|
if (pos > 0 && temp[pos - 1] === 0) {
|
||
|
|
pos--;
|
||
|
|
length++;
|
||
|
|
}
|
||
|
|
const nalu = pos === -1 ? temp : temp.subarray(0, pos);
|
||
|
|
temp = pos === -1 ? null : temp.subarray(pos + length);
|
||
|
|
if (nalu.byteLength) nalus.push(nalu);
|
||
|
|
}
|
||
|
|
return nalus;
|
||
|
|
}
|