@m7mdxzx1/discord-video-strem 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/README.md +294 -0
  2. package/dist/client/GatewayEvents.d.ts +27 -0
  3. package/dist/client/GatewayEvents.js +1 -0
  4. package/dist/client/GatewayOpCodes.d.ts +40 -0
  5. package/dist/client/GatewayOpCodes.js +41 -0
  6. package/dist/client/Streamer.d.ts +41 -0
  7. package/dist/client/Streamer.js +189 -0
  8. package/dist/client/encryptor/TransportEncryptor.d.ts +16 -0
  9. package/dist/client/encryptor/TransportEncryptor.js +38 -0
  10. package/dist/client/index.d.ts +4 -0
  11. package/dist/client/index.js +4 -0
  12. package/dist/client/packet/AudioPacketizer.d.ts +8 -0
  13. package/dist/client/packet/AudioPacketizer.js +22 -0
  14. package/dist/client/packet/BaseMediaPacketizer.d.ts +109 -0
  15. package/dist/client/packet/BaseMediaPacketizer.js +243 -0
  16. package/dist/client/packet/VideoPacketizerAnnexB.d.ts +132 -0
  17. package/dist/client/packet/VideoPacketizerAnnexB.js +231 -0
  18. package/dist/client/packet/VideoPacketizerVP8.d.ts +15 -0
  19. package/dist/client/packet/VideoPacketizerVP8.js +56 -0
  20. package/dist/client/packet/index.d.ts +4 -0
  21. package/dist/client/packet/index.js +4 -0
  22. package/dist/client/processing/AnnexBHelper.d.ts +93 -0
  23. package/dist/client/processing/AnnexBHelper.js +132 -0
  24. package/dist/client/voice/BaseMediaConnection.d.ts +118 -0
  25. package/dist/client/voice/BaseMediaConnection.js +319 -0
  26. package/dist/client/voice/MediaUdp.d.ts +26 -0
  27. package/dist/client/voice/MediaUdp.js +140 -0
  28. package/dist/client/voice/StreamConnection.d.ts +10 -0
  29. package/dist/client/voice/StreamConnection.js +30 -0
  30. package/dist/client/voice/VoiceConnection.d.ts +7 -0
  31. package/dist/client/voice/VoiceConnection.js +10 -0
  32. package/dist/client/voice/VoiceMessageTypes.d.ts +136 -0
  33. package/dist/client/voice/VoiceMessageTypes.js +1 -0
  34. package/dist/client/voice/VoiceOpCodes.d.ts +21 -0
  35. package/dist/client/voice/VoiceOpCodes.js +22 -0
  36. package/dist/client/voice/index.d.ts +5 -0
  37. package/dist/client/voice/index.js +5 -0
  38. package/dist/index.d.ts +5 -0
  39. package/dist/index.js +5 -0
  40. package/dist/media/AudioStream.d.ts +25 -0
  41. package/dist/media/AudioStream.js +63 -0
  42. package/dist/media/BaseMediaStream.d.ts +31 -0
  43. package/dist/media/BaseMediaStream.js +145 -0
  44. package/dist/media/LibavCodecId.d.ts +541 -0
  45. package/dist/media/LibavCodecId.js +552 -0
  46. package/dist/media/LibavDecoder.d.ts +5 -0
  47. package/dist/media/LibavDecoder.js +63 -0
  48. package/dist/media/LibavDemuxer.d.ts +23 -0
  49. package/dist/media/LibavDemuxer.js +295 -0
  50. package/dist/media/VideoStream.d.ts +7 -0
  51. package/dist/media/VideoStream.js +10 -0
  52. package/dist/media/index.d.ts +1 -0
  53. package/dist/media/index.js +1 -0
  54. package/dist/media/newApi.d.ts +126 -0
  55. package/dist/media/newApi.js +387 -0
  56. package/dist/media/utils.d.ts +1 -0
  57. package/dist/media/utils.js +4 -0
  58. package/dist/utils.d.ts +28 -0
  59. package/dist/utils.js +54 -0
  60. package/package.json +69 -0
  61. package/src/client/GatewayEvents.ts +41 -0
  62. package/src/client/GatewayOpCodes.ts +40 -0
  63. package/src/client/Streamer.ts +279 -0
  64. package/src/client/encryptor/TransportEncryptor.ts +62 -0
  65. package/src/client/index.ts +4 -0
  66. package/src/client/packet/AudioPacketizer.ts +28 -0
  67. package/src/client/packet/BaseMediaPacketizer.ts +307 -0
  68. package/src/client/packet/VideoPacketizerAnnexB.ts +263 -0
  69. package/src/client/packet/VideoPacketizerVP8.ts +73 -0
  70. package/src/client/packet/index.ts +4 -0
  71. package/src/client/processing/AnnexBHelper.ts +142 -0
  72. package/src/client/voice/BaseMediaConnection.ts +407 -0
  73. package/src/client/voice/MediaUdp.ts +171 -0
  74. package/src/client/voice/StreamConnection.ts +33 -0
  75. package/src/client/voice/VoiceConnection.ts +15 -0
  76. package/src/client/voice/VoiceMessageTypes.ts +164 -0
  77. package/src/client/voice/VoiceOpCodes.ts +21 -0
  78. package/src/client/voice/index.ts +5 -0
  79. package/src/index.ts +5 -0
  80. package/src/media/AudioStream.ts +81 -0
  81. package/src/media/BaseMediaStream.ts +173 -0
  82. package/src/media/LibavCodecId.ts +566 -0
  83. package/src/media/LibavDecoder.ts +82 -0
  84. package/src/media/LibavDemuxer.ts +348 -0
  85. package/src/media/VideoStream.ts +15 -0
  86. package/src/media/index.ts +1 -0
  87. package/src/media/newApi.ts +618 -0
  88. package/src/media/utils.ts +6 -0
  89. package/src/utils.ts +77 -0
@@ -0,0 +1,263 @@
1
+ import type { MediaUdp } from "../voice/MediaUdp.js";
2
+ import { BaseMediaPacketizer } from "./BaseMediaPacketizer.js";
3
+ import {
4
+ H264Helpers,
5
+ H265Helpers,
6
+ type AnnexBHelpers
7
+ } from "../processing/AnnexBHelper.js";
8
+ import { extensions } from "../../utils.js";
9
+ import { splitNalu } from "../processing/AnnexBHelper.js";
10
+ import { CodecPayloadType } from "../voice/BaseMediaConnection.js";
11
+
12
+ /**
13
+ * Annex B format
14
+ *
15
+ * Packetizer for Annex B NAL. This method does NOT support aggregation packets
16
+ * where multiple NALs are sent as a single RTP payload. The supported payload
17
+ * type is Single NAL Unit Packet and Fragmentation Unit A (FU-A). The headers
18
+ * produced correspond to packetization-mode=1.
19
+
20
+ RTP Payload Format for H.264 Video:
21
+ https://tools.ietf.org/html/rfc6184
22
+
23
+ RTP Payload Format for HEVC Video:
24
+ https://tools.ietf.org/html/rfc7798
25
+
26
+ FFmpeg H264/HEVC RTP packetisation code:
27
+ https://github.com/FFmpeg/FFmpeg/blob/master/libavformat/rtpenc_h264_hevc.c
28
+
29
+ When the payload size is less than or equal to max RTP payload, send as
30
+ Single NAL Unit Packet:
31
+ https://tools.ietf.org/html/rfc6184#section-5.6
32
+ https://tools.ietf.org/html/rfc7798#section-4.4.1
33
+
34
+ 0 1 2 3
35
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
36
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
37
+ |F|NRI| Type | |
38
+ +-+-+-+-+-+-+-+-+ |
39
+ | |
40
+ | Bytes 2..n of a single NAL unit |
41
+ | |
42
+ | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
43
+ | :...OPTIONAL RTP padding |
44
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
45
+
46
+ Type = 24 for STAP-A (NOTE: this is the type of the RTP header
47
+ and NOT the NAL type).
48
+
49
+ When the payload size is greater than max RTP payload, send as
50
+ Fragmentation Unit A (FU-A):
51
+ https://tools.ietf.org/html/rfc6184#section-5.8
52
+ 0 1 2 3
53
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
54
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
55
+ | FU indicator | FU header | |
56
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
57
+ | Fragmentation Unit (FU) Payload
58
+ |
59
+ ...
60
+ */
61
+ class VideoPacketizerAnnexB extends BaseMediaPacketizer {
62
+ private _nalFunctions: AnnexBHelpers;
63
+
64
+ constructor(connection: MediaUdp, ssrc: number, payloadType: number, nalFunctions: AnnexBHelpers) {
65
+ super(connection, ssrc, payloadType, true);
66
+ this._nalFunctions = nalFunctions;
67
+ }
68
+
69
+ /**
70
+ * Sends packets after partitioning the video frame into
71
+ * MTU-sized chunks
72
+ * @param frame Annex B video frame
73
+ */
74
+ public override async sendFrame(frame: Buffer, frametime: number): Promise<void> {
75
+ super.sendFrame(frame, frametime);
76
+
77
+ const nalus = splitNalu(frame);
78
+
79
+ let packetsSent = 0;
80
+ let bytesSent = 0;
81
+ let index = 0;
82
+ for (const nalu of nalus) {
83
+ const isLastNal = index === nalus.length - 1;
84
+ if (nalu.length <= this.mtu) {
85
+ // Send as Single NAL Unit Packet.
86
+ const packetHeader = Buffer.concat([this.makeRtpHeader(isLastNal), this.createExtensionHeader(extensions)]);
87
+
88
+ const [ciphertext, nonceBuffer] = await this.encryptData(
89
+ Buffer.concat([this.createExtensionPayload(extensions), nalu]), packetHeader
90
+ );
91
+ const packet = Buffer.concat([
92
+ packetHeader, ciphertext,
93
+ nonceBuffer.subarray(0, 4),
94
+ ]);
95
+ this.mediaUdp.sendPacket(packet);
96
+ packetsSent++;
97
+ bytesSent += packet.length;
98
+ } else {
99
+ const [naluHeader, naluData] = this._nalFunctions.splitHeader(nalu);
100
+ const data = this.partitionDataMTUSizedChunks(naluData);
101
+ const encryptedPackets: Promise<Buffer>[] = [];
102
+
103
+ // Send as Fragmentation Unit A (FU-A):
104
+ for (let i = 0; i < data.length; i++) {
105
+ const isFirstPacket = i === 0;
106
+ const isFinalPacket = i === data.length - 1;
107
+
108
+ const markerBit = isLastNal && isFinalPacket;
109
+
110
+ const packetHeader = Buffer.concat([this.makeRtpHeader(markerBit), this.createExtensionHeader(extensions)]);
111
+
112
+ const packetData = Buffer.concat([
113
+ this.createExtensionPayload(extensions),
114
+ this.makeFragmentationUnitHeader(
115
+ isFirstPacket,
116
+ isFinalPacket,
117
+ naluHeader
118
+ ),
119
+ data[i]
120
+ ]);
121
+
122
+ encryptedPackets.push(this.encryptData(packetData, packetHeader)
123
+ .then(([ciphertext, nonceBuffer]) => Buffer.concat([
124
+ packetHeader,
125
+ ciphertext,
126
+ nonceBuffer.subarray(0, 4),
127
+ ]))
128
+ );
129
+ }
130
+
131
+ for (const packet of await Promise.all(encryptedPackets))
132
+ {
133
+ this.mediaUdp.sendPacket(packet);
134
+ packetsSent++;
135
+ bytesSent += packet.length;
136
+ }
137
+ }
138
+ index++;
139
+ }
140
+
141
+ await this.onFrameSent(packetsSent, bytesSent, frametime);
142
+ }
143
+
144
+ protected makeFragmentationUnitHeader(isFirstPacket: boolean, isLastPacket: boolean, naluHeader: Buffer): Buffer {
145
+ throw new Error("Not implemented");
146
+ }
147
+
148
+ public override async onFrameSent(packetsSent: number, bytesSent: number, frametime: number): Promise<void> {
149
+ await super.onFrameSent(packetsSent, bytesSent, frametime);
150
+ // video RTP packet timestamp incremental value = 90,000Hz / fps
151
+ this.incrementTimestamp(90000 / 1000 * frametime);
152
+ }
153
+ }
154
+
155
+ export class VideoPacketizerH264 extends VideoPacketizerAnnexB {
156
+ constructor(connection: MediaUdp, ssrc: number) {
157
+ super(connection, ssrc, CodecPayloadType.H264.payload_type, H264Helpers);
158
+ }
159
+ /**
160
+ * The FU indicator octet has the following format:
161
+
162
+ +---------------+
163
+ |0|1|2|3|4|5|6|7|
164
+ +-+-+-+-+-+-+-+-+
165
+ |F|NRI| Type |
166
+ +---------------+
167
+
168
+ F and NRI bits come from the NAL being transmitted.
169
+ Type = 28 for FU-A (NOTE: this is the type of the H264 RTP header
170
+ and NOT the NAL type).
171
+
172
+ The FU header has the following format:
173
+
174
+ +---------------+
175
+ |0|1|2|3|4|5|6|7|
176
+ +-+-+-+-+-+-+-+-+
177
+ |S|E|R| Type |
178
+ +---------------+
179
+
180
+ S: Set to 1 for the start of the NAL FU (i.e. first packet in frame).
181
+ E: Set to 1 for the end of the NAL FU (i.e. the last packet in the frame).
182
+ R: Reserved bit must be 0.
183
+ Type: The NAL unit payload type, comes from NAL packet (NOTE: this IS the type of the NAL message).
184
+ * @param isFirstPacket
185
+ * @param isLastPacket
186
+ * @param naluHeader
187
+ * @returns FU-A packets
188
+ */
189
+ protected override makeFragmentationUnitHeader(isFirstPacket: boolean, isLastPacket: boolean, naluHeader: Buffer): Buffer {
190
+ const nal0 = naluHeader[0];
191
+ const fuPayloadHeader = Buffer.alloc(2);
192
+ const nalType = H264Helpers.getUnitType(naluHeader);
193
+ const fnri = nal0 & 0xe0;
194
+
195
+ // set fu indicator
196
+ fuPayloadHeader[0] = 0x1c | fnri; // type 28 with fnri from original frame
197
+
198
+ // set fu header
199
+ if (isFirstPacket) {
200
+ fuPayloadHeader[1] = 0x80 | nalType; // set start bit
201
+ } else if (isLastPacket) {
202
+ fuPayloadHeader[1] = 0x40 | nalType; // set last bit
203
+ } else {
204
+ fuPayloadHeader[1] = nalType; // no start or end bit
205
+ }
206
+
207
+ return fuPayloadHeader;
208
+ }
209
+ }
210
+
211
+ export class VideoPacketizerH265 extends VideoPacketizerAnnexB {
212
+ constructor(connection: MediaUdp, ssrc: number) {
213
+ super(connection, ssrc, CodecPayloadType.H265.payload_type, H265Helpers);
214
+ }
215
+ /**
216
+ * The FU indicator octet has the following format:
217
+
218
+ +---------------+---------------+
219
+ |0|1|2|3|4|5|6|7|0|1|2|3|4|5|6|7|
220
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
221
+ |F| Type | LayerId | TID |
222
+ +-------------+-----------------+
223
+
224
+ All other fields except Type come from the NAL being transmitted.
225
+ Type = 49 for FU-A (NOTE: this is the type of the H265 RTP header
226
+ and NOT the NAL type).
227
+
228
+ The FU header has the following format:
229
+
230
+ +---------------+
231
+ |0|1|2|3|4|5|6|7|
232
+ +-+-+-+-+-+-+-+-+
233
+ |S|E| Type |
234
+ +---------------+
235
+
236
+ S: Set to 1 for the start of the NAL FU (i.e. first packet in frame).
237
+ E: Set to 1 for the end of the NAL FU (i.e. the last packet in the frame).
238
+ Type: The NAL unit payload type, comes from NAL packet (NOTE: this IS the type of the NAL message).
239
+ * @param isFirstPacket
240
+ * @param isLastPacket
241
+ * @param naluHeader
242
+ * @returns FU-A packets
243
+ */
244
+ protected override makeFragmentationUnitHeader(isFirstPacket: boolean, isLastPacket: boolean, naluHeader: Buffer): Buffer {
245
+ const fuIndicatorHeader = Buffer.allocUnsafe(3);
246
+ naluHeader.copy(fuIndicatorHeader);
247
+ const nalType = H265Helpers.getUnitType(naluHeader);
248
+
249
+ // clear NAL type and set it to 49
250
+ fuIndicatorHeader[0] = (fuIndicatorHeader[0] & 0b10000001) | (49 << 1);
251
+
252
+ // set fu header
253
+ if (isFirstPacket) {
254
+ fuIndicatorHeader[2] = 0x80 | nalType; // set start bit
255
+ } else if (isLastPacket) {
256
+ fuIndicatorHeader[2] = 0x40 | nalType; // set last bit
257
+ } else {
258
+ fuIndicatorHeader[2] = nalType; // no start or end bit
259
+ }
260
+
261
+ return fuIndicatorHeader;
262
+ }
263
+ }
@@ -0,0 +1,73 @@
1
+ import type { MediaUdp } from "../voice/MediaUdp.js";
2
+ import { extensions, max_int16bit } from "../../utils.js";
3
+ import { BaseMediaPacketizer } from "./BaseMediaPacketizer.js";
4
+ import { CodecPayloadType } from "../voice/BaseMediaConnection.js";
5
+
6
+ /**
7
+ * VP8 payload format
8
+ *
9
+ */
10
+ export class VideoPacketizerVP8 extends BaseMediaPacketizer {
11
+ private _pictureId: number;
12
+
13
+ constructor(connection: MediaUdp, ssrc: number) {
14
+ super(connection, ssrc, CodecPayloadType.VP8.payload_type, true);
15
+ this._pictureId = 0;
16
+ }
17
+
18
+ private incrementPictureId(): void {
19
+ this._pictureId = (this._pictureId + 1) % max_int16bit;
20
+ }
21
+
22
+ public override async sendFrame(frame: Buffer, frametime: number): Promise<void> {
23
+ super.sendFrame(frame, frametime);
24
+ const data = this.partitionDataMTUSizedChunks(frame);
25
+
26
+ let bytesSent = 0;
27
+ const encryptedPackets = data.map((chunk, i) => this.createPacket(chunk, i === (data.length - 1), i === 0))
28
+ for (const packet of await Promise.all(encryptedPackets)) {
29
+ this.mediaUdp.sendPacket(packet);
30
+ bytesSent += packet.length;
31
+ }
32
+
33
+ await this.onFrameSent(data.length, bytesSent, frametime);
34
+ }
35
+
36
+ public async createPacket(chunk: Buffer, isLastPacket = true, isFirstPacket = true): Promise<Buffer> {
37
+ if(chunk.length > this.mtu) throw Error('error packetizing video frame: frame is larger than mtu');
38
+
39
+ const packetHeader = Buffer.concat([this.makeRtpHeader(isLastPacket), this.createExtensionHeader(extensions)]);
40
+
41
+ const packetData = Buffer.concat([this.createExtensionPayload(extensions), this.makeChunk(chunk, isFirstPacket)]);
42
+
43
+ // nonce buffer used for encryption. 4 bytes are appended to end of packet
44
+ const [ciphertext, nonceBuffer] = await this.encryptData(packetData, packetHeader);
45
+ return Buffer.concat([packetHeader, ciphertext, nonceBuffer.subarray(0, 4)]);
46
+ }
47
+
48
+ public override async onFrameSent(packetsSent: number, bytesSent: number, frametime: number): Promise<void> {
49
+ await super.onFrameSent(packetsSent, bytesSent, frametime);
50
+ // video RTP packet timestamp incremental value = 90,000Hz / fps
51
+ this.incrementTimestamp(90000 / 1000 * frametime);
52
+ this.incrementPictureId();
53
+ }
54
+
55
+ private makeChunk(frameData: Buffer, isFirstPacket: boolean): Buffer {
56
+ // vp8 payload descriptor
57
+ const payloadDescriptorBuf = Buffer.alloc(2);
58
+
59
+ payloadDescriptorBuf[0] = 0x80;
60
+ payloadDescriptorBuf[1] = 0x80;
61
+ if (isFirstPacket) {
62
+ payloadDescriptorBuf[0] |= 0b00010000; // mark S bit, indicates start of frame
63
+ }
64
+
65
+ // vp8 pictureid payload extension
66
+ const pictureIdBuf = Buffer.alloc(2);
67
+
68
+ pictureIdBuf.writeUIntBE(this._pictureId, 0, 2);
69
+ pictureIdBuf[0] |= 0b10000000;
70
+
71
+ return Buffer.concat([payloadDescriptorBuf, pictureIdBuf, frameData]);
72
+ }
73
+ }
@@ -0,0 +1,4 @@
1
+ export * from './AudioPacketizer.js';
2
+ export * from './BaseMediaPacketizer.js';
3
+ export * from './VideoPacketizerVP8.js';
4
+ export * from './VideoPacketizerAnnexB.js';
@@ -0,0 +1,142 @@
1
+ export enum H264NalUnitTypes {
2
+ Unspecified = 0,
3
+ CodedSliceNonIDR = 1,
4
+ CodedSlicePartitionA = 2,
5
+ CodedSlicePartitionB = 3,
6
+ CodedSlicePartitionC = 4,
7
+ CodedSliceIdr = 5,
8
+ SEI = 6,
9
+ SPS = 7,
10
+ PPS = 8,
11
+ AccessUnitDelimiter = 9,
12
+ EndOfSequence = 10,
13
+ EndOfStream = 11,
14
+ FillerData = 12,
15
+ SEIExtenstion = 13,
16
+ PrefixNalUnit = 14,
17
+ SubsetSPS = 15,
18
+ }
19
+
20
+ export enum H265NalUnitTypes {
21
+ TRAIL_N = 0,
22
+ TRAIL_R = 1,
23
+ TSA_N = 2,
24
+ TSA_R = 3,
25
+ STSA_N = 4,
26
+ STSA_R = 5,
27
+ RADL_N = 6,
28
+ RADL_R = 7,
29
+ RASL_N = 8,
30
+ RASL_R = 9,
31
+ RSV_VCL_N10 = 10,
32
+ RSV_VCL_R11 = 11,
33
+ RSV_VCL_N12 = 12,
34
+ RSV_VCL_R13 = 13,
35
+ RSV_VCL_N14 = 14,
36
+ RSV_VCL_R15 = 15,
37
+ BLA_W_LP = 16,
38
+ BLA_W_RADL = 17,
39
+ BLA_N_LP = 18,
40
+ IDR_W_RADL = 19,
41
+ IDR_N_LP = 20,
42
+ CRA_NUT = 21,
43
+ RSV_IRAP_VCL22 = 22,
44
+ RSV_IRAP_VCL23 = 23,
45
+ RSV_VCL24 = 24,
46
+ RSV_VCL25 = 25,
47
+ RSV_VCL26 = 26,
48
+ RSV_VCL27 = 27,
49
+ RSV_VCL28 = 28,
50
+ RSV_VCL29 = 29,
51
+ RSV_VCL30 = 30,
52
+ RSV_VCL31 = 31,
53
+ VPS_NUT = 32,
54
+ SPS_NUT = 33,
55
+ PPS_NUT = 34,
56
+ AUD_NUT = 35,
57
+ EOS_NUT = 36,
58
+ EOB_NUT = 37,
59
+ FD_NUT = 38,
60
+ PREFIX_SEI_NUT = 39,
61
+ SUFFIX_SEI_NUT = 40,
62
+ RSV_NVCL41 = 41,
63
+ RSV_NVCL42 = 42,
64
+ RSV_NVCL43 = 43,
65
+ RSV_NVCL44 = 44,
66
+ RSV_NVCL45 = 45,
67
+ RSV_NVCL46 = 46,
68
+ RSV_NVCL47 = 47,
69
+ UNSPEC48 = 48,
70
+ UNSPEC49 = 49,
71
+ UNSPEC50 = 50,
72
+ UNSPEC51 = 51,
73
+ UNSPEC52 = 52,
74
+ UNSPEC53 = 53,
75
+ UNSPEC54 = 54,
76
+ UNSPEC55 = 55,
77
+ UNSPEC56 = 56,
78
+ UNSPEC57 = 57,
79
+ UNSPEC58 = 58,
80
+ UNSPEC59 = 59,
81
+ UNSPEC60 = 60,
82
+ UNSPEC61 = 61,
83
+ UNSPEC62 = 62,
84
+ UNSPEC63 = 63,
85
+ };
86
+
87
+ export interface AnnexBHelpers {
88
+ getUnitType(frame: Buffer): number;
89
+ splitHeader(frame: Buffer): [Buffer, Buffer];
90
+ isAUD(unitType: number): boolean;
91
+ }
92
+
93
+ export const H264Helpers: AnnexBHelpers = {
94
+ getUnitType(frame) {
95
+ return frame[0] & 0x1f;
96
+ },
97
+ splitHeader(frame) {
98
+ return [frame.subarray(0, 1), frame.subarray(1)];
99
+ },
100
+ isAUD(unitType) {
101
+ return unitType === H264NalUnitTypes.AccessUnitDelimiter;
102
+ }
103
+ }
104
+
105
+ export const H265Helpers: AnnexBHelpers = {
106
+ getUnitType(frame) {
107
+ return (frame[0] >> 1) & 0x3f;
108
+ },
109
+ splitHeader(frame) {
110
+ return [frame.subarray(0, 2), frame.subarray(2)];
111
+ },
112
+ isAUD(unitType) {
113
+ return unitType === H265NalUnitTypes.AUD_NUT;
114
+ }
115
+ }
116
+
117
+ // Get individual NAL units from an AVPacket frame
118
+ export function splitNalu(frame: Buffer) {
119
+ const nalus = [];
120
+ let offset = 0;
121
+ while (offset < frame.length) {
122
+ const naluSize = frame.readUInt32BE(offset);
123
+ offset += 4;
124
+ const nalu = frame.subarray(offset, offset + naluSize);
125
+ nalus.push(nalu);
126
+ offset += nalu.length;
127
+ }
128
+ return nalus;
129
+ }
130
+
131
+ // Merge NAL units into an AVPacket frame
132
+ export function mergeNalu(nalus: Buffer[])
133
+ {
134
+ const chunks = [];
135
+ for (const nalu of nalus)
136
+ {
137
+ const size = Buffer.allocUnsafe(4);
138
+ size.writeUInt32BE(nalu.length);
139
+ chunks.push(size, nalu);
140
+ }
141
+ return Buffer.concat(chunks);
142
+ }