feat: Voice Gateway v8

This commit is contained in:
Elysia
2025-03-02 18:28:47 +07:00
parent 33b507fc6f
commit 756ec458bc
23 changed files with 740 additions and 334 deletions

View File

@@ -20,7 +20,7 @@ Please use the @dank074/discord-video-stream library for the best support.
const EventEmitter = require('events');
const { Readable: ReadableStream } = require('stream');
const prism = require('prism-media');
const { H264NalSplitter } = require('./processing/AnnexBNalSplitter');
const { H264NalSplitter, H265NalSplitter } = require('./processing/AnnexBNalSplitter');
const { IvfTransformer } = require('./processing/IvfSplitter');
const { H264Dispatcher } = require('../dispatcher/AnnexBDispatcher');
const AudioDispatcher = require('../dispatcher/AudioDispatcher');
@@ -62,6 +62,19 @@ const FFMPEG_H264_ARGUMENTS = options => [
'h264_metadata=aud=insert',
];
const FFMPEG_H265_ARGUMENTS = options => [
'-c:v',
'libx265',
'-f',
'hevc',
'-preset',
options?.presetH265 || 'faster',
'-profile:v',
'main',
'-bf',
'0',
];
/**
* Player for a Voice Connection.
* @private
@@ -188,15 +201,19 @@ class MediaPlayer extends EventEmitter {
}
// Get stream type
if (this.voiceConnection.videoCodec == 'VP8') {
if (this.voiceConnection.videoCodec === 'VP8') {
args.push(...FFMPEG_VP8_ARGUMENTS);
// Remove '-speed', '5' bc bad quality
}
if (this.voiceConnection.videoCodec == 'H264') {
if (this.voiceConnection.videoCodec === 'H264') {
args.push(...FFMPEG_H264_ARGUMENTS(options));
}
if (this.voiceConnection.videoCodec === 'H265') {
args.push(...FFMPEG_H265_ARGUMENTS(options));
}
args.push('-force_key_frames', '00:02');
if (options?.inputFFmpegArgs) {
@@ -229,7 +246,7 @@ class MediaPlayer extends EventEmitter {
return this.playAnnexBVideo(ffmpeg, options, streams, 'H264');
}
default: {
throw new Error('Invalid codec (Supported: VP8, H264)');
throw new Error('Invalid codec (Supported: VP8, H264, H265)');
}
}
}
@@ -247,7 +264,12 @@ class MediaPlayer extends EventEmitter {
// eslint-disable-next-line no-unused-vars
playAnnexBVideo(stream, options, streams, type) {
this.destroyVideoDispatcher();
const videoStream = new H264NalSplitter();
let videoStream;
if (type === 'H264') {
videoStream = new H264NalSplitter();
} else if (type === 'H265') {
videoStream = new H265NalSplitter();
}
stream.pipe(videoStream);
streams.video = videoStream;
const dispatcher = this.createVideoDispatcher(options, streams);

View File

@@ -0,0 +1,37 @@
'use strict';
const { Buffer } = require('node:buffer');
const { Transform } = require('node:stream');
class PCMInsertSilence extends Transform {
constructor(options) {
super(options);
// 48Khz, 2 channels, 16-bit (2 bytes per channel)
this.sampleRate = 48000;
this.channels = 2;
// 4 bytes per frame (2 channels * 2 bytes)
this.bytesPerFrame = this.channels * 2;
this.lastChunkTime = Date.now();
this.silenceThresholdMs = 50;
}
_transform(chunk, encoding, callback) {
const now = Date.now();
const gap = now - this.lastChunkTime;
if (gap >= this.silenceThresholdMs) {
const missingFrames = Math.floor((gap / 1000) * this.sampleRate);
const silenceBuffer = Buffer.alloc(missingFrames * this.bytesPerFrame, 0);
this.push(silenceBuffer);
}
this.lastChunkTime = now;
this.push(chunk);
callback();
}
}
module.exports = {
PCMInsertSilence,
};