chore: clean code + add options

This commit is contained in:
Elysia
2024-07-29 19:01:40 +07:00
parent 772739f980
commit 55df05d4d8
7 changed files with 42 additions and 38 deletions

View File

@@ -46,7 +46,9 @@ client.on('ready', async client => {
const input = 'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4'; const input = 'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4';
// Play // Play
const dispatcher = stream.playVideo(input, { const dispatcher = stream.playVideo(input, {
fps: 30, fps: 60,
bitrate: 4000,
resolution: '1080',
}); });
const dispatcher2 = stream.playAudio(input); const dispatcher2 = stream.playAudio(input);
dispatcher.on('start', () => { dispatcher.on('start', () => {

View File

@@ -29,7 +29,6 @@ class AnnexBDispatcher extends VideoDispatcher {
codecCallback(frame) { codecCallback(frame) {
let accessUnit = frame; let accessUnit = frame;
const nalus = [];
let offset = 0; let offset = 0;
// Extract NALUs from the access unit // Extract NALUs from the access unit
@@ -37,25 +36,19 @@ class AnnexBDispatcher extends VideoDispatcher {
const naluSize = accessUnit.readUInt32BE(offset); const naluSize = accessUnit.readUInt32BE(offset);
offset += 4; offset += 4;
const nalu = accessUnit.subarray(offset, offset + naluSize); const nalu = accessUnit.subarray(offset, offset + naluSize);
nalus.push(nalu); const isLastNal = offset + naluSize >= accessUnit.length;
offset += naluSize;
}
nalus.forEach((nalu, index) => {
const isLastNal = index === nalus.length - 1;
if (nalu.length <= this.mtu) { if (nalu.length <= this.mtu) {
// If NALU size is within MTU, send it directly // If NALU size is within MTU, send it directly
this._playChunk(Buffer.concat([this.createHeaderExtension(), nalu]), index + 1 === nalus.length); this._playChunk(Buffer.concat([this.createHeaderExtension(), nalu]), isLastNal);
} else { } else {
// If NALU size exceeds MTU, fragment it // If NALU size exceeds MTU, fragment it
const [naluHeader, naluData] = this._nalFunctions.splitHeader(nalu); const [naluHeader, naluData] = this._nalFunctions.splitHeader(nalu);
const dataFragments = this.partitionVideoData(naluData); const dataFragments = this.partitionVideoData(naluData);
dataFragments.forEach((data, fragmentIndex) => { for (let fragmentIndex = 0; fragmentIndex < dataFragments.length; fragmentIndex++) {
const data = dataFragments[fragmentIndex];
const isFirstPacket = fragmentIndex === 0; const isFirstPacket = fragmentIndex === 0;
const isFinalPacket = fragmentIndex === dataFragments.length - 1; const isFinalPacket = fragmentIndex === dataFragments.length - 1;
const markerBit = isLastNal && isFinalPacket; // Is last packet ?
this._playChunk( this._playChunk(
Buffer.concat([ Buffer.concat([
@@ -63,11 +56,12 @@ class AnnexBDispatcher extends VideoDispatcher {
this.makeFragmentationUnitHeader(isFirstPacket, isFinalPacket, naluHeader), this.makeFragmentationUnitHeader(isFirstPacket, isFinalPacket, naluHeader),
data, data,
]), ]),
markerBit, isLastNal && isFinalPacket,
); );
}); }
} }
}); offset += naluSize;
}
} }
} }

View File

@@ -316,12 +316,12 @@ class BaseDispatcher extends Writable {
_createPacket(buffer, isLastPacket = false) { _createPacket(buffer, isLastPacket = false) {
// Header // Header
const packetBuffer = Buffer.alloc(12); const packetBuffer = Buffer.alloc(12);
packetBuffer[0] = (2 << 6) | ((this.extensionEnabled ? 1 : 0) << 4); packetBuffer[0] = this.extensionEnabled ? 0x90 : 0x80; // 0b10000000 | ((this.extensionEnabled ? 1 : 0) << 4);
packetBuffer[1] = this.payloadType; packetBuffer[1] = this.payloadType;
if (this.extensionEnabled) { if (this.extensionEnabled) {
if (isLastPacket) { if (isLastPacket) {
packetBuffer[1] |= 0b10000000; packetBuffer[1] |= 0x80;
} }
} }

View File

@@ -25,27 +25,24 @@ class VP8Dispatcher extends VideoDispatcher {
super(player, highWaterMark, streams, fps); super(player, highWaterMark, streams, fps);
} }
makeChunk(buffer, i) { makeChunk(buffer, isFirstFrame) {
// Make frame // Make frame
const headerExtensionBuf = this.createHeaderExtension(); const headerExtensionBuf = this.createHeaderExtension();
// Vp8 payload descriptor // Vp8 payload descriptor
const payloadDescriptorBuf = Buffer.alloc(2); const payloadDescriptorBuf = Buffer.alloc(2);
payloadDescriptorBuf[0] = 0x80; payloadDescriptorBuf[0] = isFirstFrame ? 0x90 : 0x80; // Mark S bit, indicates start of frame: payloadDescriptorBuf[0] |= 0b00010000;
payloadDescriptorBuf[1] = 0x80; payloadDescriptorBuf[1] = 0x80;
if (i == 0) {
payloadDescriptorBuf[0] |= 0b00010000; // Mark S bit, indicates start of frame
}
// Vp8 pictureid payload extension // Vp8 pictureid payload extension
const pictureIdBuf = Buffer.alloc(2); const pictureIdBuf = Buffer.alloc(2);
pictureIdBuf.writeUIntBE(this.count, 0, 2); pictureIdBuf.writeUintBE(this.count, 0, 2);
pictureIdBuf[0] |= 0b10000000; pictureIdBuf[0] |= 0x80;
return Buffer.concat([headerExtensionBuf, payloadDescriptorBuf, pictureIdBuf, buffer]); return Buffer.concat([headerExtensionBuf, payloadDescriptorBuf, pictureIdBuf, buffer]);
} }
codecCallback(chunk) { codecCallback(chunk) {
const chunkSplit = this.partitionVideoData(chunk); const chunkSplit = this.partitionVideoData(chunk);
for (let i = 0; i < chunkSplit.length; i++) { for (let i = 0; i < chunkSplit.length; i++) {
this._playChunk(this.makeChunk(chunkSplit[i], i), i + 1 === chunkSplit.length); this._playChunk(this.makeChunk(chunkSplit[i], i == 0), i + 1 === chunkSplit.length);
} }
} }
} }

View File

@@ -132,6 +132,14 @@ class MediaPlayer extends EventEmitter {
`${options?.fps}`, `${options?.fps}`,
]; ];
if (options?.resolution && options?.resolution !== 'maximum') {
args.push('-vf', `scale=-1:${options.resolution}`);
}
if (options?.bitrate && typeof options?.bitrate === 'number') {
args.push('-b:v', `${options?.bitrate}K`);
}
if (!isStream) { if (!isStream) {
args[1] = input; args[1] = input;
} }
@@ -144,28 +152,28 @@ class MediaPlayer extends EventEmitter {
// Get stream type // Get stream type
if (this.voiceConnection.videoCodec == 'VP8') { if (this.voiceConnection.videoCodec == 'VP8') {
args.push('-f', 'ivf', '-deadline', 'realtime', '-c:v', options?.copy ? 'copy' : 'libvpx'); args.push('-f', 'ivf', '-deadline', 'realtime', '-c:v', 'libvpx');
// Remove '-speed', '5' bc bad quality // Remove '-speed', '5' bc bad quality
} }
if (this.voiceConnection.videoCodec == 'H264') { if (this.voiceConnection.videoCodec == 'H264') {
args.push( args.push(
'-c:v', '-c:v',
options?.copy ? 'copy' : 'libx264', 'libx264',
'-f', '-f',
'h264', 'h264',
'-tune', '-tune',
'zerolatency', 'zerolatency',
'-pix_fmt', // '-pix_fmt',
'yuv420p', // 'yuv420p',
'-preset', '-preset',
options?.preset || 'faster', options?.presetH26x || 'faster',
'-profile:v', '-profile:v',
'baseline', 'baseline',
'-g', // '-g',
`${options?.fps}`, // `${options?.fps}`,
'-x264-params', // '-x264-params',
`keyint=${options?.fps}:min-keyint=${options?.fps}`, // `keyint=${options?.fps}:min-keyint=${options?.fps}`,
'-bf', '-bf',
'0', '0',
'-bsf:v', '-bsf:v',

View File

@@ -79,17 +79,19 @@ class PlayInterface {
* @typedef {Object} VideoOptions * @typedef {Object} VideoOptions
* @property {number} [seek=0] The time to seek to, will be ignored when playing `ogg/opus` or `webm/opus` streams * @property {number} [seek=0] The time to seek to, will be ignored when playing `ogg/opus` or `webm/opus` streams
* @property {number} [fps=30] Video fps * @property {number} [fps=30] Video fps
* @property {boolean} [copy=false] Copy codec ? * @property {'maximum' | '480' | '720' | '1080' | '1440'} [resolution='maximum'] Resoluion (Height)
* @property {number} [highWaterMark=12] The maximum number of opus packets to make and store before they are * @property {number} [highWaterMark=12] The maximum number of opus packets to make and store before they are
* actually needed. See https://nodejs.org/en/docs/guides/backpressuring-in-streams/. Setting this value to * actually needed. See https://nodejs.org/en/docs/guides/backpressuring-in-streams/. Setting this value to
* 1 means that changes in volume will be more instant. * 1 means that changes in volume will be more instant.
* @property {'ultrafast' | 'superfast' | 'veryfast' | 'faster' | 'fast' | 'medium' | 'slow' | 'slower' | 'veryslow'} [preset='veryfast'] ffmpeg preset * @property {'ultrafast' | 'superfast' | 'veryfast' | 'faster' | 'fast' | 'medium' | 'slow' | 'slower' | 'veryslow'} [presetH26x='veryfast'] ffmpeg preset h254 / h265
* @property {boolean} [hwAccel=false] Enables hardware accelerated video decoding. Enabling this option might result in an exception * @property {boolean} [hwAccel=false] Enables hardware accelerated video decoding. Enabling this option might result in an exception
* being thrown by Ffmpeg process if your system does not support hardware acceleration * being thrown by Ffmpeg process if your system does not support hardware acceleration
* @property {string[]} [inputFFmpegArgs] input ffmpeg * @property {string[]} [inputFFmpegArgs] input ffmpeg
* Ex: ['-config1', 'value1', '-config2', 'value2'] * Ex: ['-config1', 'value1', '-config2', 'value2']
* @property {string[]} [outputFFmpegArgs] output ffmpeg * @property {string[]} [outputFFmpegArgs] output ffmpeg
* Ex: ['-config1', 'value1', '-config2', 'value2'] * Ex: ['-config1', 'value1', '-config2', 'value2']
* @property {number|'auto'} [bitrate=2000] The bitrate (quality) of the video in kbps.
* If set to 'auto', ffmpeg will automatically select
*/ */
/** /**

5
typings/index.d.ts vendored
View File

@@ -932,10 +932,11 @@ export interface VideoOptions {
highWaterMark?: number; highWaterMark?: number;
fps?: number; fps?: number;
hwAccel?: boolean; hwAccel?: boolean;
preset?: 'ultrafast' | 'superfast' | 'veryfast' | 'faster' | 'fast' | 'medium' | 'slow' | 'slower' | 'veryslow'; presetH26x?: 'ultrafast' | 'superfast' | 'veryfast' | 'faster' | 'fast' | 'medium' | 'slow' | 'slower' | 'veryslow';
copy?: boolean;
inputFFmpegArgs?: string[]; inputFFmpegArgs?: string[];
outputFFmpegArgs?: string[]; outputFFmpegArgs?: string[];
resolution?: 'maximum' | '480' | '720' | '1080' | '1440';
bitrate?: number | 'auto';
} }
export class BaseDispatcher extends Writable { export class BaseDispatcher extends Writable {