Skip to content

Commit fe918ed

Browse files
committed
Add resolution capping and bitrate override options (#148)
Add new configuration options to control stream output: STREAM_BITRATE_OVERRIDE to force using configured bitrate instead of video's original, and STREAM_MAX_WIDTH/STREAM_MAX_HEIGHT to cap maximum resolution. These options allow finer control over streaming quality and resource usage.
1 parent 2d065fa commit fe918ed

8 files changed

Lines changed: 77 additions & 11 deletions

File tree

.env.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,11 @@ YTDLP_COOKIES_PATH = "" # Path to cookies file for yt-dlp (Netscape format). Use
1515

1616
# Stream options
1717
STREAM_RESPECT_VIDEO_PARAMS = "false" # This option is used to respect video parameters such as width, height, fps, bitrate, and max bitrate.
18+
STREAM_BITRATE_OVERRIDE = "false" # If true, use STREAM_BITRATE_KBPS even when respecting video params
1819
STREAM_WIDTH = "1280" # The width of the video stream in pixels
1920
STREAM_HEIGHT = "720" # The height of the video stream in pixels
21+
STREAM_MAX_WIDTH = "0" # Max width cap (0 = disabled)
22+
STREAM_MAX_HEIGHT = "0" # Max height cap (0 = disabled)
2023
STREAM_FPS = "30" # The frames per second (FPS) of the video stream
2124
STREAM_BITRATE_KBPS = "2000" # The bitrate of the video stream in kilobits per second (Kbps)
2225
STREAM_MAX_BITRATE_KBPS = "2500" # The maximum bitrate of the video stream in kilobits per second (Kbps)

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,8 +242,11 @@ YTDLP_COOKIES_PATH=""
242242
```bash
243243
# Video Quality Settings
244244
STREAM_RESPECT_VIDEO_PARAMS="false" # Use original video parameters if true
245+
STREAM_BITRATE_OVERRIDE="false" # If true, use STREAM_BITRATE_KBPS even when respecting video params
245246
STREAM_WIDTH="1280" # Output resolution width
246247
STREAM_HEIGHT="720" # Output resolution height
248+
STREAM_MAX_WIDTH="0" # Max width cap (0 = disabled)
249+
STREAM_MAX_HEIGHT="0" # Max height cap (0 = disabled)
247250
STREAM_FPS="30" # Target frame rate
248251

249252
# Bitrate Settings (affects quality and bandwidth usage)

docker-compose-node.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ services:
2020

2121
# Stream options
2222
STREAM_RESPECT_VIDEO_PARAMS: "false" # This option is used to respect video parameters such as width, height, fps, bitrate, and max bitrate.
23+
STREAM_BITRATE_OVERRIDE: "false" # If true, use STREAM_BITRATE_KBPS even when respecting video params
24+
STREAM_MAX_WIDTH: "0" # Max width cap (0 = disabled)
25+
STREAM_MAX_HEIGHT: "0" # Max height cap (0 = disabled)
2326
STREAM_WIDTH: "1280" # The width of the video stream in pixels
2427
STREAM_HEIGHT: "720" # The height of the video stream in pixels
2528
STREAM_FPS: "30" # The frames per second (FPS) of the video stream

docker-compose-warp.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ services:
2020

2121
# Stream options
2222
STREAM_RESPECT_VIDEO_PARAMS: "false" # This option is used to respect video parameters such as width, height, fps, bitrate, and max bitrate.
23+
STREAM_BITRATE_OVERRIDE: "false" # If true, use STREAM_BITRATE_KBPS even when respecting video params
24+
STREAM_MAX_WIDTH: "0" # Max width cap (0 = disabled)
25+
STREAM_MAX_HEIGHT: "0" # Max height cap (0 = disabled)
2326
STREAM_WIDTH: "1280" # The width of the video stream in pixels
2427
STREAM_HEIGHT: "720" # The height of the video stream in pixels
2528
STREAM_FPS: "30" # The frames per second (FPS) of the video stream

docker-compose.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ services:
2020

2121
# Stream options
2222
STREAM_RESPECT_VIDEO_PARAMS: "false" # This option is used to respect video parameters such as width, height, fps, bitrate, and max bitrate.
23+
STREAM_BITRATE_OVERRIDE: "false" # If true, use STREAM_BITRATE_KBPS even when respecting video params
24+
STREAM_MAX_WIDTH: "0" # Max width cap (0 = disabled)
25+
STREAM_MAX_HEIGHT: "0" # Max height cap (0 = disabled)
2326
STREAM_WIDTH: "1280" # The width of the video stream in pixels
2427
STREAM_HEIGHT: "720" # The height of the video stream in pixels
2528
STREAM_FPS: "30" # The frames per second (FPS) of the video stream

src/commands/config.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,14 @@ export default class ConfigCommand extends BaseCommand {
4141
const configInfo = [
4242
"**Stream Options:**",
4343
`• respect_video_params: ${config.respect_video_params}`,
44+
`• bitrateOverride: ${config.bitrateOverride}`,
4445
`• width: ${config.width}`,
4546
`• height: ${config.height}`,
4647
`• fps: ${config.fps}`,
4748
`• bitrateKbps: ${config.bitrateKbps}`,
4849
`• maxBitrateKbps: ${config.maxBitrateKbps}`,
50+
`• maxWidth: ${config.maxWidth || 'None'}`,
51+
`• maxHeight: ${config.maxHeight || 'None'}`,
4952
`• hardwareAcceleratedDecoding: ${config.hardwareAcceleratedDecoding}`,
5053
`• h26xPreset: ${config.h26xPreset}`,
5154
`• videoCodec: ${config.videoCodec}`,
@@ -90,6 +93,7 @@ export default class ConfigCommand extends BaseCommand {
9093
switch (key) {
9194
// Boolean parameters
9295
case 'respect_video_params':
96+
case 'bitrateOverride':
9397
case 'hardwareAcceleratedDecoding':
9498
const boolValue = parseBoolean(value);
9599
(config as any)[key] = boolValue;
@@ -103,11 +107,22 @@ export default class ConfigCommand extends BaseCommand {
103107
case 'fps':
104108
case 'bitrateKbps':
105109
case 'maxBitrateKbps':
110+
case 'maxWidth':
111+
case 'maxHeight':
106112
const numValue = parseInt(value);
107-
if (isNaN(numValue) || numValue <= 0) {
108-
await this.sendError(context.message, `Invalid number value: ${value}`);
113+
114+
// Validate non-negative
115+
if (isNaN(numValue) || numValue < 0) {
116+
await this.sendError(context.message, `Invalid number value: ${value}. Must be non-negative.`);
109117
return;
110118
}
119+
120+
// Validate positive for essential params
121+
if (['width', 'height', 'fps', 'bitrateKbps', 'maxBitrateKbps'].includes(key) && numValue === 0) {
122+
await this.sendError(context.message, `Invalid number value: ${value}. Must be greater than 0.`);
123+
return;
124+
}
125+
111126
(config as any)[key] = numValue;
112127
await this.sendSuccess(context.message, `Set ${key} to \`${numValue}\``);
113128
logger.info(`Config updated: ${key} = ${numValue}`);

src/config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,14 @@ export default {
8181

8282
// Stream options
8383
respect_video_params: process.env.STREAM_RESPECT_VIDEO_PARAMS ? parseBoolean(process.env.STREAM_RESPECT_VIDEO_PARAMS) : false,
84+
bitrateOverride: process.env.STREAM_BITRATE_OVERRIDE ? parseBoolean(process.env.STREAM_BITRATE_OVERRIDE) : false,
8485
width: process.env.STREAM_WIDTH ? parseInt(process.env.STREAM_WIDTH) : 1280,
8586
height: process.env.STREAM_HEIGHT ? parseInt(process.env.STREAM_HEIGHT) : 720,
8687
fps: process.env.STREAM_FPS ? parseInt(process.env.STREAM_FPS) : 30,
8788
bitrateKbps: process.env.STREAM_BITRATE_KBPS ? parseInt(process.env.STREAM_BITRATE_KBPS) : 1000,
8889
maxBitrateKbps: process.env.STREAM_MAX_BITRATE_KBPS ? parseInt(process.env.STREAM_MAX_BITRATE_KBPS) : 2500,
90+
maxWidth: process.env.STREAM_MAX_WIDTH ? parseInt(process.env.STREAM_MAX_WIDTH) : 0,
91+
maxHeight: process.env.STREAM_MAX_HEIGHT ? parseInt(process.env.STREAM_MAX_HEIGHT) : 0,
8992
hardwareAcceleratedDecoding: process.env.STREAM_HARDWARE_ACCELERATION ? parseBoolean(process.env.STREAM_HARDWARE_ACCELERATION) : false,
9093
h26xPreset: process.env.STREAM_H26X_PRESET ? parsePreset(process.env.STREAM_H26X_PRESET) : 'ultrafast',
9194
videoCodec: process.env.STREAM_VIDEO_CODEC ? parseVideoCodec(process.env.STREAM_VIDEO_CODEC) : 'H264',

src/services/streaming.ts

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -152,14 +152,21 @@ export class StreamingService {
152152
await this.playVideo(message, queueItem.url, queueItem.title, videoParams);
153153
}
154154

155-
private async getVideoParameters(videoUrl: string): Promise<{ width: number, height: number, fps?: number, bitrate?: string } | undefined> {
155+
private async getVideoParameters(videoUrl: string): Promise<{ width: number, height: number, fps?: number, bitrate?: number } | undefined> {
156156
try {
157157
const resolution = await getVideoParams(videoUrl);
158-
logger.info(`Video parameters: ${resolution.width}x${resolution.height}, FPS: ${resolution.fps || 'unknown'}`);
158+
logger.info(`Video parameters: ${resolution.width}x${resolution.height}, FPS: ${resolution.fps || 'unknown'}, Bitrate: ${resolution.bitrate || 'unknown'}`);
159+
160+
let bitrateKbps: number | undefined;
161+
if (resolution.bitrate) {
162+
bitrateKbps = Math.round(parseInt(resolution.bitrate) / 1000);
163+
}
164+
159165
return {
160166
width: resolution.width,
161167
height: resolution.height,
162-
fps: resolution.fps
168+
fps: resolution.fps,
169+
bitrate: bitrateKbps
163170
};
164171
} catch (error) {
165172
await ErrorUtils.handleError(error, 'determining video parameters');
@@ -189,12 +196,38 @@ export class StreamingService {
189196
}
190197
}
191198

192-
private setupStreamConfiguration(videoParams?: { width: number, height: number, fps?: number, bitrate?: string }): any {
199+
private setupStreamConfiguration(videoParams?: { width: number, height: number, fps?: number, bitrate?: number }): any {
200+
let width = videoParams?.width || config.width;
201+
let height = videoParams?.height || config.height;
202+
let frameRate = videoParams?.fps || config.fps;
203+
let bitrateVideo = config.bitrateKbps;
204+
205+
// If respecting video params, use video bitrate unless overridden
206+
if (videoParams && videoParams.bitrate && !config.bitrateOverride) {
207+
bitrateVideo = videoParams.bitrate;
208+
}
209+
210+
// Resolution capping
211+
if (config.maxWidth > 0 || config.maxHeight > 0) {
212+
const ratio = width / height;
213+
if (config.maxWidth > 0 && width > config.maxWidth) {
214+
width = config.maxWidth;
215+
height = Math.round(width / ratio);
216+
}
217+
if (config.maxHeight > 0 && height > config.maxHeight) {
218+
height = config.maxHeight;
219+
width = Math.round(height * ratio);
220+
}
221+
// Ensure even dimensions
222+
width = Math.round(width / 2) * 2;
223+
height = Math.round(height / 2) * 2;
224+
}
225+
193226
return {
194-
width: videoParams?.width || config.width,
195-
height: videoParams?.height || config.height,
196-
frameRate: videoParams?.fps || config.fps,
197-
bitrateVideo: config.bitrateKbps,
227+
width,
228+
height,
229+
frameRate,
230+
bitrateVideo,
198231
bitrateVideoMax: config.maxBitrateKbps,
199232
videoCodec: Utils.normalizeVideoCodec(config.videoCodec),
200233
hardwareAcceleratedDecoding: config.hardwareAcceleratedDecoding,
@@ -337,7 +370,7 @@ export class StreamingService {
337370
}
338371
}
339372

340-
public async playVideo(message: Message, videoSource: string, title?: string, videoParams?: { width: number, height: number, fps?: number, bitrate?: string }): Promise<void> {
373+
public async playVideo(message: Message, videoSource: string, title?: string, videoParams?: { width: number, height: number, fps?: number, bitrate?: number }): Promise<void> {
341374
const [guildId, channelId] = [config.guildId, config.videoChannelId];
342375
this.streamStatus.manualStop = false;
343376

0 commit comments

Comments
 (0)