fix: normalize desktop capture dimensions
This commit is contained in:
parent
0f096a588d
commit
0a0d04f3a7
@ -20,6 +20,32 @@ func normalizeRTPPort(port int) int {
|
|||||||
return port
|
return port
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func normalizeVideoDimension(value, fallback int) int {
|
||||||
|
if value <= 0 {
|
||||||
|
value = fallback
|
||||||
|
}
|
||||||
|
if value%2 != 0 {
|
||||||
|
value--
|
||||||
|
}
|
||||||
|
if value < 2 {
|
||||||
|
return fallback
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizePipelineConfig(cfg PipelineConfig) PipelineConfig {
|
||||||
|
cfg.Port = normalizeRTPPort(cfg.Port)
|
||||||
|
cfg.Width = normalizeVideoDimension(cfg.Width, 1280)
|
||||||
|
cfg.Height = normalizeVideoDimension(cfg.Height, 720)
|
||||||
|
if cfg.FPS <= 0 {
|
||||||
|
cfg.FPS = 30
|
||||||
|
}
|
||||||
|
if cfg.Bitrate <= 0 {
|
||||||
|
cfg.Bitrate = 2000
|
||||||
|
}
|
||||||
|
return cfg
|
||||||
|
}
|
||||||
|
|
||||||
// PipelineManager manages the screen capture process lifecycle
|
// PipelineManager manages the screen capture process lifecycle
|
||||||
type PipelineManager struct {
|
type PipelineManager struct {
|
||||||
cmd *exec.Cmd
|
cmd *exec.Cmd
|
||||||
@ -58,19 +84,7 @@ func (pm *PipelineManager) Start(cfg PipelineConfig) error {
|
|||||||
cfg.Display = ":0.0"
|
cfg.Display = ":0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cfg.Port = normalizeRTPPort(cfg.Port)
|
cfg = normalizePipelineConfig(cfg)
|
||||||
if cfg.Width <= 0 {
|
|
||||||
cfg.Width = 1280
|
|
||||||
}
|
|
||||||
if cfg.Height <= 0 {
|
|
||||||
cfg.Height = 720
|
|
||||||
}
|
|
||||||
if cfg.FPS <= 0 {
|
|
||||||
cfg.FPS = 30
|
|
||||||
}
|
|
||||||
if cfg.Bitrate <= 0 {
|
|
||||||
cfg.Bitrate = 2000
|
|
||||||
}
|
|
||||||
|
|
||||||
tool, args, err := pm.resolvePipeline(cfg)
|
tool, args, err := pm.resolvePipeline(cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -172,13 +186,18 @@ func (pm *PipelineManager) hasExecutable(name string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (pm *PipelineManager) buildGStreamer(cfg PipelineConfig) (string, []string, error) {
|
func (pm *PipelineManager) buildGStreamer(cfg PipelineConfig) (string, []string, error) {
|
||||||
|
cfg = normalizePipelineConfig(cfg)
|
||||||
var pipelineParts []string
|
var pipelineParts []string
|
||||||
|
|
||||||
// 1. Capture Source (X11)
|
// 1. Capture Source (X11)
|
||||||
pipelineParts = append(pipelineParts, fmt.Sprintf("ximagesrc display-name=%s", cfg.Display))
|
pipelineParts = append(pipelineParts, fmt.Sprintf("ximagesrc display-name=%s", cfg.Display))
|
||||||
pipelineParts = append(pipelineParts, "video/x-raw,framerate=30/1")
|
pipelineParts = append(pipelineParts, fmt.Sprintf("video/x-raw,framerate=%d/1", cfg.FPS))
|
||||||
pipelineParts = append(pipelineParts, "videoconvert")
|
pipelineParts = append(pipelineParts, "videoconvert")
|
||||||
pipelineParts = append(pipelineParts, "video/x-raw,format=I420")
|
pipelineParts = append(pipelineParts, "videoscale")
|
||||||
|
pipelineParts = append(
|
||||||
|
pipelineParts,
|
||||||
|
fmt.Sprintf("video/x-raw,format=I420,width=%d,height=%d,framerate=%d/1", cfg.Width, cfg.Height, cfg.FPS),
|
||||||
|
)
|
||||||
|
|
||||||
// 2. Encoder
|
// 2. Encoder
|
||||||
encoderStr := "x264enc speed-preset=ultrafast tune=zerolatency bitrate=" + fmt.Sprintf("%d", cfg.Bitrate) + " byte-stream=true key-int-max=30"
|
encoderStr := "x264enc speed-preset=ultrafast tune=zerolatency bitrate=" + fmt.Sprintf("%d", cfg.Bitrate) + " byte-stream=true key-int-max=30"
|
||||||
@ -211,6 +230,7 @@ func (pm *PipelineManager) buildGStreamer(cfg PipelineConfig) (string, []string,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (pm *PipelineManager) buildFFmpeg(cfg PipelineConfig) (string, []string, error) {
|
func (pm *PipelineManager) buildFFmpeg(cfg PipelineConfig) (string, []string, error) {
|
||||||
|
cfg = normalizePipelineConfig(cfg)
|
||||||
args := []string{
|
args := []string{
|
||||||
"-f", "x11grab",
|
"-f", "x11grab",
|
||||||
"-draw_mouse", "1",
|
"-draw_mouse", "1",
|
||||||
|
|||||||
@ -23,6 +23,8 @@ func TestBuildGStreamerUsesBrowserFriendlyH264(t *testing.T) {
|
|||||||
joined := strings.Join(args, " ")
|
joined := strings.Join(args, " ")
|
||||||
for _, expected := range []string{
|
for _, expected := range []string{
|
||||||
"video/x-raw,format=I420",
|
"video/x-raw,format=I420",
|
||||||
|
"width=1280",
|
||||||
|
"height=720",
|
||||||
"x264enc",
|
"x264enc",
|
||||||
"tune=zerolatency",
|
"tune=zerolatency",
|
||||||
"key-int-max=30",
|
"key-int-max=30",
|
||||||
@ -37,6 +39,18 @@ func TestBuildGStreamerUsesBrowserFriendlyH264(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNormalizeVideoDimensionUsesEvenValues(t *testing.T) {
|
||||||
|
if got := normalizeVideoDimension(1353, 1280); got != 1352 {
|
||||||
|
t.Fatalf("expected odd dimension to be rounded down to 1352, got %d", got)
|
||||||
|
}
|
||||||
|
if got := normalizeVideoDimension(0, 720); got != 720 {
|
||||||
|
t.Fatalf("expected fallback for zero dimension, got %d", got)
|
||||||
|
}
|
||||||
|
if got := normalizeVideoDimension(1, 720); got != 720 {
|
||||||
|
t.Fatalf("expected fallback for too-small dimension, got %d", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestBuildFFmpegUsesBrowserFriendlyH264(t *testing.T) {
|
func TestBuildFFmpegUsesBrowserFriendlyH264(t *testing.T) {
|
||||||
pm := NewPipelineManager()
|
pm := NewPipelineManager()
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user