chore: clean code + add options
This commit is contained in:
@@ -46,7 +46,9 @@ client.on('ready', async client => {
|
||||
const input = 'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4';
|
||||
// Play
|
||||
const dispatcher = stream.playVideo(input, {
|
||||
fps: 30,
|
||||
fps: 60,
|
||||
bitrate: 4000,
|
||||
resolution: '1080',
|
||||
});
|
||||
const dispatcher2 = stream.playAudio(input);
|
||||
dispatcher.on('start', () => {
|
||||
|
||||
@@ -29,7 +29,6 @@ class AnnexBDispatcher extends VideoDispatcher {
|
||||
|
||||
codecCallback(frame) {
|
||||
let accessUnit = frame;
|
||||
const nalus = [];
|
||||
let offset = 0;
|
||||
|
||||
// Extract NALUs from the access unit
|
||||
@@ -37,25 +36,19 @@ class AnnexBDispatcher extends VideoDispatcher {
|
||||
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;
|
||||
|
||||
const isLastNal = offset + naluSize >= accessUnit.length;
|
||||
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);
|
||||
this._playChunk(Buffer.concat([this.createHeaderExtension(), nalu]), isLastNal);
|
||||
} else {
|
||||
// If NALU size exceeds MTU, fragment it
|
||||
const [naluHeader, naluData] = this._nalFunctions.splitHeader(nalu);
|
||||
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 isFinalPacket = fragmentIndex === dataFragments.length - 1;
|
||||
const markerBit = isLastNal && isFinalPacket; // Is last packet ?
|
||||
|
||||
this._playChunk(
|
||||
Buffer.concat([
|
||||
@@ -63,11 +56,12 @@ class AnnexBDispatcher extends VideoDispatcher {
|
||||
this.makeFragmentationUnitHeader(isFirstPacket, isFinalPacket, naluHeader),
|
||||
data,
|
||||
]),
|
||||
markerBit,
|
||||
isLastNal && isFinalPacket,
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
offset += naluSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -316,12 +316,12 @@ class BaseDispatcher extends Writable {
|
||||
_createPacket(buffer, isLastPacket = false) {
|
||||
// Header
|
||||
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;
|
||||
|
||||
if (this.extensionEnabled) {
|
||||
if (isLastPacket) {
|
||||
packetBuffer[1] |= 0b10000000;
|
||||
packetBuffer[1] |= 0x80;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,27 +25,24 @@ class VP8Dispatcher extends VideoDispatcher {
|
||||
super(player, highWaterMark, streams, fps);
|
||||
}
|
||||
|
||||
makeChunk(buffer, i) {
|
||||
makeChunk(buffer, isFirstFrame) {
|
||||
// Make frame
|
||||
const headerExtensionBuf = this.createHeaderExtension();
|
||||
// Vp8 payload descriptor
|
||||
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;
|
||||
if (i == 0) {
|
||||
payloadDescriptorBuf[0] |= 0b00010000; // Mark S bit, indicates start of frame
|
||||
}
|
||||
// Vp8 pictureid payload extension
|
||||
const pictureIdBuf = Buffer.alloc(2);
|
||||
pictureIdBuf.writeUIntBE(this.count, 0, 2);
|
||||
pictureIdBuf[0] |= 0b10000000;
|
||||
pictureIdBuf.writeUintBE(this.count, 0, 2);
|
||||
pictureIdBuf[0] |= 0x80;
|
||||
return Buffer.concat([headerExtensionBuf, payloadDescriptorBuf, pictureIdBuf, buffer]);
|
||||
}
|
||||
|
||||
codecCallback(chunk) {
|
||||
const chunkSplit = this.partitionVideoData(chunk);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,6 +132,14 @@ class MediaPlayer extends EventEmitter {
|
||||
`${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) {
|
||||
args[1] = input;
|
||||
}
|
||||
@@ -144,28 +152,28 @@ class MediaPlayer extends EventEmitter {
|
||||
|
||||
// Get stream type
|
||||
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
|
||||
}
|
||||
|
||||
if (this.voiceConnection.videoCodec == 'H264') {
|
||||
args.push(
|
||||
'-c:v',
|
||||
options?.copy ? 'copy' : 'libx264',
|
||||
'libx264',
|
||||
'-f',
|
||||
'h264',
|
||||
'-tune',
|
||||
'zerolatency',
|
||||
'-pix_fmt',
|
||||
'yuv420p',
|
||||
// '-pix_fmt',
|
||||
// 'yuv420p',
|
||||
'-preset',
|
||||
options?.preset || 'faster',
|
||||
options?.presetH26x || 'faster',
|
||||
'-profile:v',
|
||||
'baseline',
|
||||
'-g',
|
||||
`${options?.fps}`,
|
||||
'-x264-params',
|
||||
`keyint=${options?.fps}:min-keyint=${options?.fps}`,
|
||||
// '-g',
|
||||
// `${options?.fps}`,
|
||||
// '-x264-params',
|
||||
// `keyint=${options?.fps}:min-keyint=${options?.fps}`,
|
||||
'-bf',
|
||||
'0',
|
||||
'-bsf:v',
|
||||
|
||||
@@ -79,17 +79,19 @@ class PlayInterface {
|
||||
* @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} [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
|
||||
* 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.
|
||||
* @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
|
||||
* being thrown by Ffmpeg process if your system does not support hardware acceleration
|
||||
* @property {string[]} [inputFFmpegArgs] input ffmpeg
|
||||
* Ex: ['-config1', 'value1', '-config2', 'value2']
|
||||
* @property {string[]} [outputFFmpegArgs] output ffmpeg
|
||||
* 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
5
typings/index.d.ts
vendored
@@ -932,10 +932,11 @@ export interface VideoOptions {
|
||||
highWaterMark?: number;
|
||||
fps?: number;
|
||||
hwAccel?: boolean;
|
||||
preset?: 'ultrafast' | 'superfast' | 'veryfast' | 'faster' | 'fast' | 'medium' | 'slow' | 'slower' | 'veryslow';
|
||||
copy?: boolean;
|
||||
presetH26x?: 'ultrafast' | 'superfast' | 'veryfast' | 'faster' | 'fast' | 'medium' | 'slow' | 'slower' | 'veryslow';
|
||||
inputFFmpegArgs?: string[];
|
||||
outputFFmpegArgs?: string[];
|
||||
resolution?: 'maximum' | '480' | '720' | '1080' | '1440';
|
||||
bitrate?: number | 'auto';
|
||||
}
|
||||
|
||||
export class BaseDispatcher extends Writable {
|
||||
|
||||
Reference in New Issue
Block a user