package main import ( "strconv" "strings" "testing" ) // 编码器候选顺序:硬件 HEVC 最省 → 硬件 H.264 → CPU 软编 func TestEncoderCandidatesOrder(t *testing.T) { cases := []struct { hevc, h264 bool want []string }{ {true, true, []string{"hevc_qsv", "h264_qsv", "libx264"}}, {false, true, []string{"h264_qsv", "libx264"}}, {true, false, []string{"hevc_qsv", "libx264"}}, {false, false, []string{"libx264"}}, } for _, c := range cases { got := encoderCandidates(c.hevc, c.h264) if strings.Join(got, ",") != strings.Join(c.want, ",") { t.Errorf("hevc=%v h264=%v 应为 %v,实际 %v", c.hevc, c.h264, c.want, got) } } // 候选列表必须能真的跑出结果,最后一个永远是纯 CPU 的 libx264 last := encoderCandidates(true, true) if last[len(last)-1] != "libx264" { t.Errorf("最后一个候选必须是 libx264,实际 %s", last[len(last)-1]) } } // 编码器参数:固定码率 / 质量模式,HEVC 必须带 hvc1 tag(Apple 系才认) func TestEncoderOpts(t *testing.T) { join := func(ss []string) string { return strings.Join(ss, " ") } cases := []struct { encoder string bitrate int want []string }{ {"hevc_qsv", 4000, []string{"-b:v", "4000k", "-tag:v", "hvc1"}}, {"h264_qsv", 4000, []string{"-b:v", "4000k"}}, {"libx264", 4000, []string{"-b:v", "4000k", "-preset", "medium"}}, {"hevc_qsv", 0, []string{"-global_quality", "22", "-preset", "medium", "-tag:v", "hvc1"}}, {"libx265", 0, []string{"-crf", "18", "-preset", "medium", "-tag:v", "hvc1"}}, } for _, c := range cases { got := encoderOpts(c.encoder, c.bitrate) if join(got) != join(c.want) { t.Errorf("%s @ %d 应为 %v,实际 %v", c.encoder, c.bitrate, c.want, got) } } } // 「合成码率」填 0 时的算法:源片码率 + 弹幕余量,探测不到就交回 0(走质量模式) func TestAutoMergeBitrate(t *testing.T) { cases := []struct { source int want int }{ {3197, 3697}, // 用户那场录播的源片码率 {0, 0}, // 探测不到 → 质量模式 {-1, 0}, {100, 800}, // 低于下限时抬到下限 {3000, 3500}, // 正常情况就是 +500 } for _, c := range cases { if got := autoMergeBitrate(c.source); got != c.want { t.Errorf("源码率 %d 应得 %d,实际 %d", c.source, c.want, got) } } } // ffmpeg 输出的解析:分辨率靠「逗号 + 数字x数字」,码率靠视频流那一行的 kb/s, // 视频流没写码率时退回容器总码率 func TestProbeStreamRegex(t *testing.T) { sizeLine := " Stream #0:0[0x1](und): Video: h264 (High) (avc1 / 0x31637661), yuv420p(tv, bt709, progressive), 1920x1080 [SAR 1:1 DAR 16:9], 3217 kb/s, 23.98 fps" m := videoSizeRe.FindStringSubmatch(sizeLine) if len(m) != 3 { t.Fatalf("没解析出分辨率:%s", sizeLine) } w, _ := strconv.Atoi(m[1]) h, _ := strconv.Atoi(m[2]) if w != 1920 || h != 1080 { t.Errorf("分辨率解析成 %dx%d", w, h) } // 视频流那一行里的码率(不是编码器名里的 0x…) if b := videoBitrateRe.FindStringSubmatch(sizeLine); len(b) != 2 || b[1] != "3217" { t.Errorf("视频流码率没解析对:%v", b) } portrait := " Stream #0:0[0x1](und): Video: h264 (High) (avc1 / 0x31637661), yuv420p(tv, smpte170m/unknown/unknown, progressive), 720x1280 [SAR 1:1 DAR 9:16], 1570 kb/s, 10 fps" m = videoSizeRe.FindStringSubmatch(portrait) if len(m) != 3 || m[1] != "720" || m[2] != "1280" { t.Errorf("竖屏分辨率没解析对:%v", m) } // 视频流那行没写码率时用容器总码率兜底 durLine := " Duration: 00:02:55.07, start: 0.000000, bitrate: 1767 kb/s" if m := containerBitrateRe.FindStringSubmatch(durLine); len(m) != 2 || m[1] != "1767" { t.Errorf("容器码率没解析对:%v", m) } }