feat: try implement djs voice + video (v12)

This commit is contained in:
Elysia
2024-07-24 19:27:50 +07:00
parent 7fa4666df0
commit 26aa85c126
31 changed files with 3768 additions and 9 deletions

View File

@@ -0,0 +1,126 @@
'use strict';
/*
Credit: https://github.com/dank074/Discord-video-stream
The use of video streaming in this library is an incomplete implementation with many bugs, primarily aimed at lazy users.
The video streaming features in this library are sourced from https://github.com/dank074/Discord-video-stream.
Please use the @dank074/discord-video-stream library to access all advanced and professional features,
along with comprehensive support. I will not actively fix bugs related to streaming and encourage everyone to
use https://github.com/dank074/Discord-video-stream for stable and smooth streaming.
To reiterate: This is an incomplete implementation of the library https://github.com/dank074/Discord-video-stream.
Thanks to dank074 and longnguyen2004 for implementing new codecs (H264, H265).
Thanks to mrjvs for discovering how Discord transmits data and the VP8 codec.
Please use the @dank074/discord-video-stream library for the best support.
*/
const { Buffer } = require('buffer');
const VideoDispatcher = require('./VideoDispatcher');
const { H264Helpers, H265Helpers } = require('../player/processing/AnnexBNalSplitter');
class AnnexBDispatcher extends VideoDispatcher {
constructor(player, highWaterMark = 12, streams, fps, nalFunctions) {
super(player, highWaterMark, streams, fps);
this._nalFunctions = nalFunctions;
}
codecCallback(frame) {
let accessUnit = frame;
const nalus = [];
let offset = 0;
// Extract NALUs from the access unit
while (offset < accessUnit.length) {
const naluSize = accessUnit.readUInt32BE(offset);
offset += 4;
const nalu = accessUnit.subarray(offset, offset + naluSize);
nalus.push(nalu);
offset += naluSize;
}
nalus.forEach((nalu, index) => {
const isLastNal = index === nalus.length - 1;
if (nalu.length <= this.mtu) {
// If NALU size is within MTU, send it directly
this._playChunk(Buffer.concat([this.createHeaderExtension(), nalu]), index + 1 === nalus.length);
} else {
// If NALU size exceeds MTU, fragment it
const [naluHeader, naluData] = this._nalFunctions.splitHeader(nalu);
const dataFragments = this.partitionVideoData(naluData);
dataFragments.forEach((data, fragmentIndex) => {
const isFirstPacket = fragmentIndex === 0;
const isFinalPacket = fragmentIndex === dataFragments.length - 1;
const markerBit = isLastNal && isFinalPacket; // Is last packet ?
this._playChunk(
Buffer.concat([
this.createHeaderExtension(),
this.makeFragmentationUnitHeader(isFirstPacket, isFinalPacket, naluHeader),
data,
]),
markerBit,
);
});
}
});
}
}
class H264Dispatcher extends AnnexBDispatcher {
constructor(player, highWaterMark = 12, streams, fps) {
super(player, highWaterMark, streams, fps, H264Helpers);
}
makeFragmentationUnitHeader(isFirstPacket, isLastPacket, naluHeader) {
const nal0 = naluHeader[0];
const fuPayloadHeader = Buffer.alloc(2);
const nalType = H264Helpers.getUnitType(naluHeader);
const fnri = nal0 & 0xe0;
fuPayloadHeader[0] = 0x1c | fnri;
if (isFirstPacket) {
fuPayloadHeader[1] = 0x80 | nalType;
} else if (isLastPacket) {
fuPayloadHeader[1] = 0x40 | nalType;
} else {
fuPayloadHeader[1] = nalType;
}
return fuPayloadHeader;
}
}
class H265Dispatcher extends AnnexBDispatcher {
constructor(player, highWaterMark = 12, streams, fps) {
super(player, highWaterMark, streams, fps, H265Helpers);
}
makeFragmentationUnitHeader(isFirstPacket, isLastPacket, naluHeader) {
const fuIndicatorHeader = Buffer.allocUnsafe(3);
naluHeader.copy(fuIndicatorHeader);
const nalType = H265Helpers.getUnitType(naluHeader);
fuIndicatorHeader[0] = (fuIndicatorHeader[0] & 0b10000001) | (49 << 1);
if (isFirstPacket) {
fuIndicatorHeader[2] = 0x80 | nalType;
} else if (isLastPacket) {
fuIndicatorHeader[2] = 0x40 | nalType;
} else {
fuIndicatorHeader[2] = nalType;
}
return fuIndicatorHeader;
}
}
module.exports = {
H264Dispatcher,
H265Dispatcher,
};