abundant-magician-17307
12/18/2025, 9:28 AMtall-airline-16756
12/18/2025, 9:28 AMtall-airline-16756
12/18/2025, 9:28 AMadvanced 配置进行自定义(与 preset 二选一),可手动设置 `width`、`height`、`videoBitrate`、`videoCodec`(例如 `H264_HIGH`)和 `framerate`,通过提高分辨率和码率直接提升清晰度。
技术上,Universal Egress 架构使用 headless Chromium 渲染页面并由 GStreamer 做转码,这使得高质量(如 1080p)导出既能还原页面视觉内容又不会影响实时房间的性能。
如果你愿意,我可以:
• 帮你把当前的 Egress 请求体(JSON)检查并给出修改建议;或
• 推荐一个按你目标分辨率/码率的具体 advanced 配置样例。
你想先把当前导出配置贴上来,还是我直接给一个 1080p/5Mbps 的范例?
Sources: Egress API | LiveKit docs | Egress examples | LiveKit docs | Universal Egressabundant-magician-17307
12/18/2025, 9:55 AMtall-airline-16756
12/18/2025, 9:55 AMpreset 或 advanced 字段和 file_outputs 部分。
2. 页面在 headless Chromium 中的渲染分辨率(如果有设置),或你是否使用默认的渲染尺寸。
3. 导出时的视频分辨率、码率和帧率(如果你在 advanced 中有配置)。
4. 模糊表现的具体描述:是整体模糊、细节丢失、文字不清,还是有明显的缩放/拉伸痕迹?并附一张导出画面截图(如方便)。
5. 你的页面是否包含大量复杂动画、Canvas、WebGL 或动态缩放的元素?
6. 你使用的 LiveKit 版本号(确认是否确实已升级到 1.9.1 之后的版本)。
我先根据你提供的配置查看几点常见原因并给出改法:
• 若 preset 为低清或未设置,建议改为 H264_1080P_30 或使用 advanced 指定更高 `width`/`height` 与更高 `videoBitrate`(如 5_000_000)。
• 确认 headless Chromium 的渲染尺寸与目标输出分辨率一致,避免浏览器先以低分辨率渲染再放大导致模糊。
• 对含 Canvas/WebGL 的页面,确保它们按目标分辨率进行绘制(高 DPI / devicePixelRatio 处理)。
• 如果文字模糊,检查页面是否启用了缩放或 CSS transform 导致子像素失真;可尝试将页面缩放设为 1 并在渲染时设置正确的 viewport 大小。
把上述第1项(Egress 请求 JSON)贴上来,我会直接指出需要修改的字段并给出一个可复制的 1080p/5Mbps 的示例配置。abundant-magician-17307
12/18/2025, 9:58 AMtall-airline-16756
12/18/2025, 9:58 AMpreset 或 `advanced`(贴出 JSON 我帮看)。
• 记下预期分辨率(例如 1920x1080)。
2) 验证页面 viewport 与根尺寸是否一致
• 在页面 <head> 加或确认这一行:
<meta name="viewport" content="width=1920, height=1080, initial-scale=1.0, user-scalable=no">
• 在 CSS 强制根尺寸:
html, body { width:1920px; height:1080px; margin:0; padding:0; overflow:hidden; }
#app { width:100%; height:100%; }
3) 排查 CSS 缩放/transform 导致的子像素问题
• 全局搜索并临时禁用 `transform: scale(...)`、`zoom`、或任何按比例缩放的 JS。
• 若必须缩放,改为直接计算并设置整数像素宽高(不要用 small non-integer scales)。
• 检查是否有元素定位或 translate 导致小数像素(如 left: 360.5px),用 JS 四舍五入到整数像素。
4) 检查 devicePixelRatio / 高 DPI 支持
• 在模板脚本里强制 viewport 比例与渲染一致:
// 在页面初始化时
window.devicePixelRatio = 1; // 或根据需要设置,但常见做法是在渲染前确保 1:1
• 或在 Puppeteer 中设置 viewport:
await page.setViewport({ width:1920, height:1080, deviceScaleFactor:1 });
5) 本地用 headless Chrome/Puppeteer 验证(快速复现)
• 启动一个本地脚本,设置 window 大小与 viewport 为目标分辨率,然后截图并对比:
◦ 如果本地截图已模糊,问题在页面/样式;
◦ 如果本地清晰但 LiveKit 导出模糊,可能是 Egress 的渲染参数或后端转码(把 Egress 请求体发我看)。
6) 检查字体与渲染优化
• 添加或确认:
body {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility;
}
• 避免使用非常细的字体权重(如 100)或非常小的 font-size(<12px)在高分辨率导出上。
7) 使用截图对比定位问题源头
• 配置 Egress 的 image_outputs 生成 1:1 大小截图(同输出分辨率),把截图和页面设计图或浏览器正常渲染截图对比。
• 如果截图与浏览器渲染一致但视频仍模糊,检查 advanced 中的 `videoBitrate`/codec 是否过低。
如果你愿意,下一步可选(把这些贴上来我直接帮看并给修改建议):
• 把你的 Egress 请求 JSON(含 `preset`/`advanced`、`image_outputs` 配置)贴上来。
• 或上传一张 LiveKit 导出的视频截图和本地浏览器渲染的截图,我来对比并指出具体 CSS/配置要改的点。 Sources: RoomComposite Egress | LiveKit Docs | Egress examples | LiveKit docsabundant-magician-17307
12/18/2025, 10:09 AMtall-airline-16756
12/18/2025, 10:09 AMpreset 或 `advanced`、`image_outputs`/`file_outputs`、以及 layout 配置。
2) 立即核对并调整这三项(最常见原因)
• 输出分辨率必须与页面 viewport/渲染尺寸 1:1 匹配。若输出 1920x1080,页面必须以 1920x1080 渲染(见下)。
• 确保 videoBitrate 足够高(例如 5_000_000 即 5 Mbps)并使用高质量 codec(如 `H264_HIGH`)。在 advanced 中设置或选择 H264_1080P_30 预设。
• 确保 headless Chromium 的渲染没有被缩放:页面中加入正确的 viewport 与根尺寸。
3) 在你的模板里按此修改并验证(快速示例)
• head 部分:
<meta name="viewport" content="width=1920, height=1080, initial-scale=1.0, user-scalable=no">
• CSS:
html, body { width:1920px; height:1080px; margin:0; padding:0; overflow:hidden; }
#app { width:100%; height:100%; }
body { -webkit-font-smoothing:antialiased; -moz-osx-font-smoothing:grayscale; text-rendering:optimizeLegibility; }
• 避免使用 `transform: scale(...)`、`document.body.style.zoom` 或任何会触发非整数缩放的 JS。
4) 本地复现(Puppeteer 快速检测)
• 用 headless Chrome 设置 viewport 与 deviceScaleFactor 为 1,截图对比本地截图与 Egress 的截图:
◦ 如果本地 headless 截图也模糊:页面/样式问题(继续调整 CSS/viewport)。
◦ 如果本地清晰但 Egress 模糊:把 Egress JSON 和导出截图发来(可能是 Egress `advanced`/转码参数或渲染参数问题)。
5) 排查字体与像素对齐
• 避免太小字体(<12px);若使用自定义 webfont,确保字体在渲染前已加载(使用 FontFaceObserver 或 font-display:swap 配合加载等待)。
• 检查定位值是否为小数像素,必要时通过 JS 四舍五入到整数像素。
6) 如果想我直接给出可复制的示例配置
• 我可以基于 1080p/5Mbps 给出一个完整的 StartRoomCompositeEgress 示例 JSON 和对应模板片段。
下一步你选择一项并把结果贴上来:
• A) 把当前 Egress 请求 JSON 和一张导出模糊截图发给我;或
• B) 我直接发你一个 1080p/5Mbps 的完整示例(JSON + 必要模板片段)。
你选哪个?abundant-magician-17307
12/18/2025, 10:20 AM<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<link rel="icon" href="/custom/favicon.ico"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="theme-color" content="#000000"/>
<meta name="description" content="Recording interface for LiveKit"/>
<link rel="apple-touch-icon" href="/custom/logo.png"/>
<link rel="manifest" href="/custom/manifest.json"/>
<title>LiveKit Egress</title>
<script defer="defer" src="static/custom/js/main.39e49d97.js"></script>
<link href="static/custom/css/main.dd337458.css" rel="stylesheet">
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
是否正确tall-airline-16756
12/18/2025, 10:20 AM<meta name="viewport" content="width=1920, height=1080, initial-scale=1.0, user-scalable=no">
2) 在 CSS 中强制根尺寸与像素对齐
html, body {
width: 1920px;
height: 1080px;
margin: 0;
padding: 0;
overflow: hidden;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility;
}
#root { width: 100%; height: 100%; }
3) 本地快速验证(选其一)
• 用 Puppeteer 模拟 headless Chrome:
◦ setViewport({ width:1920, height:1080, deviceScaleFactor:1 }),截图比对本地截图与 Egress 导出截图。
• 如果页面有 JS 做缩放(如 `zoom`、`transform: scale()`),临时注释或移除,确认文字是否变清晰。
请把这两样贴上来,我帮你进一步确认并给出精确调整:
• 当前的 Egress 请求 JSON(含 preset 或 `advanced`、`file_outputs`/`image_outputs`);
• 一张本地浏览器渲染的截图与一张 Egress 导出的模糊截图(便于对比)。abundant-magician-17307
12/19/2025, 1:13 AM${process.env.BASE_URL}jlearn_config.js?t=${Date.now()} // 加时间戳避免缓存
script.onload = () => {
resolve(window.__BASIC_CONFIG__)
}
document.head.appendChild(script)
})
}
// 挂载到 Vue 原型
loadBasicConfig().then((config) => {
Vue.prototype.$basic = config
})
const room = new Room({
adaptiveStream: false,
// adaptiveStream: { pixelDensity: 'screen' },
dynacast: true,
publishDefaults: {
simulcast: true,
videoEncoding: ScreenSharePresets.h1080fps30.encoding,
videoSimulcastLayers: [VideoPresets.h180, VideoPresets.h1080],
videoCodec: 'h264',
screenShareSimulcastLayers: [VideoPresets.h720, VideoPresets.h1080],
screenShareEncoding: ScreenSharePresets.h1080fps30.encoding,
audioPreset: 'musicHighQualityStereo', // musicHighQualityStereo telephone
forceStereo: true // 立体声音轨
},
audioCaptureDefaults: {
echoCancellation: true, // 回声取消
noiseSuppression: true, // 噪音抑制
sampleRate: '48000Hz', // 每秒采样的次数
sampleSize: '32bit' // 每个采样点的位数
},
// 捕获的画面的清晰度
videoCaptureDefaults: {
resolution: VideoPresets.h360
}
})
// 全局后置钩子-常用于结束动画等
router.afterEach(transition => {
// NProgress结束进度条
// NProgress.done()
// console.log(transition)
})
Vue.use(Antd)
Vue.config.productionTip = false
Vue.prototype.$BASE_URL = process.env.BASE_URL
Vue.prototype.$GlobalLoader = GlobalLoader
Vue.prototype.axios = axios
Vue.prototype.$bus = new Vue()
Vue.prototype.$room = room
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')是否存在问题tall-airline-16756
12/19/2025, 1:13 AMhtml, body {
width:1920px;
height:1080px;
margin:0;
padding:0;
overflow:hidden;
-webkit-font-smoothing:antialiased;
-moz-osx-font-smoothing:grayscale;
text-rendering:optimizeLegibility;
}
#root { width:100%; height:100%; }
1. 排查并禁用任何会触发缩放的 JS/CSS
◦ 搜索并临时移除 `document.body.style.zoom`、`transform: scale(...)`、按比例计算 font-size(如基于 vw/vh/rem 的动态缩放)。
◦ 如果页面有动态按窗口大小缩放的逻辑(比如监听 resize 改算式缩放),临时注释以测试效果。
1. 确认字体加载与像素对齐
◦ 若使用 webfont,确保字体在渲染前已加载(FontFaceObserver 或等待 font ready)。
◦ 确保关键定位值为整数像素(避免 left/top 出现小数)。
1. 验证 Egress 配置
◦ 把你的 Egress 请求 JSON(含 preset 或 `advanced`、`file_outputs`/`image_outputs`)贴上来,我会检查:是否用 H264_1080P_30 或 advanced 设置了 `width`/`height`、`videoBitrate`(建议 5_000_000)和合适 codec(`H264_HIGH`)。
1. 本地快速复现测试(我推荐)
◦ 用 Puppeteer 设置 viewport 为目标分辨率并截图:
await page.setViewport({ width:1920, height:1080, deviceScaleFactor:1 });
• 对比本地 headless 截图 与 Egress 导出截图:
◦ 若本地也模糊 → 页面/样式问题(继续调整第1–4步)
◦ 若本地清晰但 Egress 模糊 → 是 Egress 的渲染或转码参数问题(把 JSON 发我)
下一步我可以直接帮你做两件事(选一项): A) 我把你当前的 Egress 请求 JSON 和一张导出模糊截图发给我,我会直接指出需要修改的字段并给出修正后的 JSON 示例。 B) 我直接给一个可复制的 1080p/5Mbps 的完整 Egress JSON 示例和必须放在模板里的最小修改。
你希望我先做 A 还是 B?abundant-magician-17307
12/19/2025, 3:09 AMegress service/service.go:138 request validated {"nodeID": "NE_fBdaXGiXEtYX", "clusterID": "", "egressID": "EG_bKVGxEVMF8Vx", "requestType": "room_composite", "outputType": "stream", "room": "60ba66c6dc964889b6623fb5361a0599", "request": {"RoomComposite":{"room_name":"60ba66c6dc964889b6623fb5361a0599","layout":"speaker","Output":{"Stream":{"urls":["<rtmp://192.168.5.24:32237/114d810/{157...452}>"]}},"Options":{"Advanced":{"width":1920,"height":1080,"framerate":30,"video_codec":2,"video_bitrate":4500,"key_frame_interval":3}},"stream_outputs":[{"urls":["<rtmp://192.168.5.24:32237/114d810/{157...452}>"]}]}}}
2025/12/19 10:53:06 2025-12-19T02:53:06.485Z DEBUG egress server/main.go:184 handler launched {"nodeID": "NE_fBdaXGiXEtYX", "handlerID": "EGH_cu94c7t7Qvzr", "clusterID": "", "egressID": "EG_bKVGxEVMF8Vx"}
2025/12/19 10:53:06 2025-12-19T02:53:06.486Z INFO egress redis/redis.go:127 connecting to redis {"nodeID": "NE_fBdaXGiXEtYX", "handlerID": "EGH_cu94c7t7Qvzr", "clusterID": "", "egressID": "EG_bKVGxEVMF8Vx", "simple": true, "addr": "192.168.10.51:6380"}
2025/12/19 10:53:06 2025-12-19T02:53:06.506Z DEBUG egress source/web.go:145 creating pulse sink {"nodeID": "NE_fBdaXGiXEtYX", "handlerID": "EGH_cu94c7t7Qvzr", "clusterID": "", "egressID": "EG_bKVGxEVMF8Vx"}
2025/12/19 10:53:06 2025-12-19T02:53:06.514Z DEBUG egress source/web.go:169 creating X display {"nodeID": "NE_fBdaXGiXEtYX", "handlerID": "EGH_cu94c7t7Qvzr", "clusterID": "", "egressID": "EG_bKVGxEVMF8Vx", "display": ":871032124", "dims": "1920x1080x24"}
2025/12/19 10:53:06 2025-12-19T02:53:06.517Z DEBUG egress source/web.go:200 launching chrome {"nodeID": "NE_fBdaXGiXEtYX", "handlerID": "EGH_cu94c7t7Qvzr", "clusterID": "", "egressID": "EG_bKVGxEVMF8Vx", "url": "<https://meetingin.hanweb.com/api-gateway/common-live-server/indexDvr?layout=speaker&token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3NjYxOTkxODYsImlzcyI6IkFQSTg1WThucmNUTTJFeCIsIm5iZiI6MTc2NjExMjc4Niwic3ViIjoiRUdfYktWR3hFVk1GOFZ4IiwidmlkZW8iOnsiY2FuUHVibGlzaCI6ZmFsc2UsImNhblB1Ymxpc2hEYXRhIjpmYWxzZSwiY2FuU3Vic2NyaWJlIjp0cnVlLCJoaWRkZW4iOnRydWUsInJlY29yZGVyIjp0cnVlLCJyb29tIjoiNjBiYTY2YzZkYzk2NDg4OWI2NjIzZmI1MzYxYTA1OTkiLCJyb29tSm9pbiI6dHJ1ZX19.cetB3TikDAljo_2SLfzgUG_YEqtsINlw7e7Z0xZ7Z7g&url=wss%3A%2F%2Fmeetingin.hanweb.com%2Fhanwebrtc%2F>", "sandbox": false, "insecure": true}
根据日志打印中的编码参数确定egress配置是否有误,使用此地址打开能预览流画面是清晰的https://meetingin.hanweb.com/api-gateway/common-live-server/indexDvr?layout=speaker&tok[…]xZ7Z7g&url=wss%3A%2F%2Fmeetingin.hanweb.com%2Fhanwebrtc%2Ftall-airline-16756
12/19/2025, 3:09 AMpreset: H264_1080P_30 或在 advanced 中指定高质量 codec/profile(如 `H264_HIGH`)。
3. 保持或调整 key frame interval:3 秒通常可接受;若直播平台要求 GOP=2s/1s,请与 RTMP 目标一致。
4. 确认 RTMP 目的端不再转码或下采样:检查 RTMP 服务器配置,确保它不会把输入流重新编码到更低分辨率或更低码率。
5. 若使用 headless Chromium 渲染页面,确保页面以 1920x1080 的 viewport 1:1 渲染(你的模板需要 meta viewport 和 CSS 固定尺寸),否则 Chromium 先缩放再编码也会模糊(你之前的检查已做)。
示例:推荐的 Advanced 配置(可直接替换日志中的 Options.Advanced)
"Advanced": {
"width": 1920,
"height": 1080,
"framerate": 30,
"video_codec": "H264_HIGH", // 或使用 preset: "H264_1080P_30"
"video_bitrate": 6000,
"key_frame_interval": 3
}
或者使用 preset(更简单且可靠):
"preset": "H264_1080P_30"
后续诊断(如果调整后仍模糊)
• 把你修改后的完整 Egress 请求 JSON 贴来(我会核查字段)。
• 提供一帧导出视频截图和本地浏览器渲染的截图以便对比,或说明 RTMP 服务器是否有转码策略。
你想我先给你替换好完整的请求 JSON 示例,还是你先把 RTMP 目标服务器的转码设置/截图发来?abundant-magician-17307
12/19/2025, 3:12 AMtall-airline-16756
12/19/2025, 3:12 AMpreset: "H264_1080P_30" 或在 advanced 中设置 `video_codec: "H264_HIGH"`。
3. RTMP 目标是否转码:确认你的 RTMP 服务器或推流链路不会在入站时对流做二次转码或下采样。若会,需在 RTMP 端关闭转码或提高其输出码率/分辨率。
4. 确认 Egress 渲染为 1:1 像素比:确保页面 viewport/CSS 已按 1920x1080 固定(之前建议的 meta + CSS 已应用),避免 Chromium 先缩放再编码。
5. 关键帧/GOP 与平台要求一致:部分转码器对 GOP/关键帧间隔敏感,若平台需要 2s 或 1s,调整 key_frame_interval。
6. 如果提高码率和改 codec 后仍模糊,抓取对比截图:
◦ 本地浏览器渲染截图(你已有,清晰)
◦ Egress 输出视频的一帧截图(模糊)
和当前完整的 Egress 请求 JSON 发来,我帮你逐字段核查并给出可直接替换的 JSON 示例。
你想先我直接给一个完整的 1080p/6000kbps 的 Egress JSON 示例,还是先把 RTMP 服务器是否做转码的情况或导出模糊截图发来?abundant-magician-17307
12/19/2025, 3:15 AMtall-airline-16756
12/19/2025, 3:15 AMpreset: H264_1080P_30 或 `video_codec: H264_HIGH`。
5) 验证 RTMP 目标服务器处理
• 操作:确认 RTMP 服务器是否对入站流做转码/降采样。
• 如何查:查看 RTMP 服务/转码器配置(如 nginx-rtmp、Wowza、FFmpeg 推断),或在 RTMP 服务器端抓取入站流的实际分辨率与码率。
• 判断:
◦ 若 RTMP 对流再编码到更低码率/分辨率 → 画质下降在 RTMP 端(需要关闭转码或提高输出配置)。
◦ 若 RTMP 不转码 → 问题在 Egress 编码参数或传输链路。
6) 端到端抓包/信息采集(诊断证据)
• 收集项(并发给我以便分析):
◦ 本地浏览器截图(A)与 headless 本地截图(B)。
◦ Egress 导出的一帧截图(C)。
◦ 完整的 Egress 请求 JSON(request body)。
◦ Egress 日志中关于编码/转码的详细条目(已贴一部分)。
◦ RTMP 服务器入站流信息(ffprobe 或日志输出,显示实际接收的 resolution/bitrate/codecs)。
7) 如果确认是 Egress 编码导致(常见修复)
• 操作:修改 Egress 请求为:
◦ 使用 preset: "H264_1080P_30" 或 Advanced 中设置 "video_bitrate": 6000 和 `"video_codec": "H264_HIGH"`。
• 测试:重新启动 Egress,比较 C 是否改善。
8) 如果确认是 RTMP/转码导致
• 操作:
◦ 在 RTMP 端关闭自动转码或调整转码参数为更高码率/分辨率。
◦ 或直接把 Egress 输出改为文件输出(先验证本地录制质量),排除 RTMP 链路影响。
9) 辅助检查(若仍未定位)
• 检查关键帧间隔(key_frame_interval)是否符合 RTMP/转码器要求(部分平台要求 2s 或更短)。
• 检查网络丢包/带宽:若带宽受限,编码器可能自适应降低画质。
• 检查 GPU/CPU 性能告警日志(若 Egress 节点负载高也可能降质)。
下一步建议(最有效的起点)
• 请先做第2步(用 Puppeteer 本地 headless 截图 B)并把 A/B/C 三张截图和完整 Egress 请求 JSON 发来,我会直接帮你对比并给出可复制的 JSON 修改建议。abundant-magician-17307
12/19/2025, 3:22 AMtall-airline-16756
12/19/2025, 3:22 AMAdvanced 或 `preset`、`file_outputs`、`layout` 等)。 2) 提供一帧从导出文件截取的模糊画面截图(PNG/JPEG)。 3) 在本地用 Puppeteer 做 headless 渲染并截图:
• 脚本示例:
await page.setViewport({ width:1920, height:1080, deviceScaleFactor:1 }); await page.goto('<同样的 URL>'); await page.screenshot({ path: 'headless.png' });
• 把生成的 headless.png 发来。
4) 确认或贴出模板中的 meta viewport 与关键 CSS(已建议的固定 1920x1080 设置),以及是否存在任何运行时缩放(`zoom`、`transform: scale()`、按 vw/vh/rem 动态缩放的逻辑)。 5) 可选:把 Egress 节点的 CPU/GPU 负载或错误日志片段发来(若有资源不足可能触发降质)。
我会比对三张图片(本地浏览器、headless、本次导出)和请求 JSON,直接给出:
• 必要的 Advanced 字段修正(码率、codec/preset、key_frame_interval)或替换为 preset: "H264_1080P_30" 的完整 JSON;
• 若是渲染问题,具体的 template/CSS/JS 修正建议;
• 若是节点性能问题,我会列出需要的监控值与建议阈值。
把第1–3项发来,我马上帮你对比并给出可直接使用的修复配置。abundant-magician-17307
12/19/2025, 3:43 AMtall-airline-16756
12/19/2025, 3:43 AM-light 变体)。要使用它们,只需在调用 Room Composite Egress 时不提供 `custom_base_url`,并指定你要的 `layout`,系统会自动使用官方模板渲染。
使用官方模板的优点
• 官方模板经过测试并为 headless Chromium 渲染优化,能避免常见的 viewport/CSS/缩放导致的文字模糊或拉伸问题。
• 省去你维护自定义前端模板时可能引入的渲染细节错误,适合用来快速排查是否为自定义模板导致的画质问题。
但请注意
• 官方模板能排除模板引起的渲染问题,但最终输出质量仍取决于 Egress 的编码参数(preset/advanced 中的分辨率、码率、codec、关键帧间隔等)和后端传输链路(如 RTMP 服务器是否会二次转码)。
• 推荐同时做对比测试:先用官方模板进行一次文件输出(或 image_outputs 截图),若输出清晰则说明自定义模板存在渲染适配问题;若仍模糊则重点检查 Egress 的 `preset`/`advanced`(提高 `video_bitrate`、使用 H264_1080P_30 或 `H264_HIGH`)和下游转码链路。
如果你希望,我可以:
• 给出一个直接可用的 StartRoomCompositeEgress 示例(使用官方 speaker 模板且按 1080p/6000kbps 配置),你可以直接提交测试;或
• 帮你对比你当前自定义模板与官方模板的导出差异(你把当前 Egress 请求 JSON 和导出截图发来)。
你希望我先发示例配置,还是帮你对比现有请求与输出?
Sources: Composite and web recordings | LiveKit docs | Custom recording templates | LiveKit docs | Egress examples | LiveKit docsabundant-magician-17307
12/19/2025, 3:45 AMtall-airline-16756
12/19/2025, 3:46 AM{
"room_name": "<your-room>",
"layout": "speaker",
"file_outputs": [
{
"filepath": "recording.mp4"
}
],
"preset": "H264_1080P_30"
}
• 这一步最能快速判断问题是否由你自定义模板引起。
3) 若要自托管官方模板包(替换或调试用)
• clone 并构建:
a. git clone https://github.com/livekit/egress.git
b. 进入 template-default 目录,按仓库 README 进行构建(通常是 npm/yarn 构建生成静态文件)。
c. 将构建产物部署到可被 Egress 访问的 URL,并在 StartRoomCompositeEgress 请求中设置 custom_base_url 指向该 URL。
• 这样你可以在官方模板基础上做最小改动并复现问题。
4) 我可以帮你做的两件事(你选一项)
• A) 发给你一个可直接使用的完整 StartRoomCompositeEgress JSON(使用官方 speaker 模板且 1080p/6000kbps),你可以直接提交测试。
• B) 指导你如何 clone + 构建 template-default,并给出部署与调试要点(包括确保 meta viewport 与固定 CSS)。
你想要 A 还是 B?abundant-magician-17307
12/19/2025, 3:51 AMlayout就可以了吧。不需要单独下载并配置模板包tall-airline-16756
12/19/2025, 3:51 AM"speaker" 或 `"grid"`),Egress 会自动使用内置模板渲染与录制。
• 不需要单独下载、构建或额外配置模板包即可直接使用官方模板进行测试和排查渲染问题。
小提示:即便使用内置模板,仍需确认你的 Egress 请求编码参数(preset/advanced 中的分辨率、码率、codec)满足质量要求;如果你需要,我可以直接给一个建议的 1080p/6000kbps 的请求示例。abundant-magician-17307
12/19/2025, 3:56 AMDEBUG egress source/web.go:200 launching chrome {"nodeID": "NE_gqSHRoGwPu57", "handlerID": "EGH_YUm8rFUABGMc", "clusterID": "", "egressID": "EG_DZnfxNRqCGyv", "url": "<http://localhost:7980/?layout=speaker&token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3NjYyMDI4OTUsImlzcyI6IkFQSTg1WThucmNUTTJFeCIsIm5iZiI6MTc2NjExNjQ5NSwic3ViIjoiRUdfRFpuZnhOUnFDR3l2IiwidmlkZW8iOnsiY2FuUHVibGlzaCI6ZmFsc2UsImNhblB1Ymxpc2hEYXRhIjpmYWxzZSwiY2FuU3Vic2NyaWJlIjp0cnVlLCJoaWRkZW4iOnRydWUsInJlY29yZGVyIjp0cnVlLCJyb29tIjoiNjBiYTY2YzZkYzk2NDg4OWI2NjIzZmI1MzYxYTA1OTkiLCJyb29tSm9pbiI6dHJ1ZX19.B8k679b-u0C23VL07qEICOXpfsLCDv4C0TIYXU_1H-Y&url=wss%3A%2F%2Fmeetingin.hanweb.com%2Fhanwebrtc%2F>", "sandbox": false, "insecure": true}
2025/12/19 11:55:00
url已经变了,但是画面还是模糊tall-airline-16756
12/19/2025, 3:57 AMAdvanced 或 `preset`、`file_outputs`/`stream_outputs`、`layout`)。
• 我会基于该 JSON 直接给出修正后的示例(如改为 preset:H264_1080P_30 或 video_bitrate=6000、video_codec=H264_HIGH 等)。
2) 当前导出的示例证据
• 发一帧从导出文件或流抓取的模糊截图,和本地浏览器(非 headless)渲染的清晰截图作对比。
3) 调整并优先尝试(你可以先直接修改并重试)
• 将 video_bitrate 提升到 6000(或 6000–8000)kbps。
• 或直接使用 `preset: "H264_1080P_30"`。
• 在 Advanced 中把 video_codec 改为 `H264_HIGH`(如支持)。
• 保持 width/height = 1920/1080,framerate = 30,key_frame_interval 按目标平台要求(3s 通常可行)。
4) 验证 Egress 节点是否在转码或受限
• 检查启动 Egress 的主机 CPU/GPU 负载(在导出时是否接近饱和)。
• 查看 Egress 节点日志中是否有转码失败、降级或资源告警条目。
5) 如果你使用 file 输出并修改后仍模糊
• 说明确实是 Egress 编码器参数或节点资源问题;把修改后的 JSON、导出截图和节点负载信息发我,我会逐项定位并提供最终可用的 JSON。
你先把第1项(完整请求 JSON)和一张导出模糊截图发来,我会立刻给出可复制的 1080p/6000kbps 的完整请求示例并指出需要修改的地方。abundant-magician-17307
12/19/2025, 3:59 AMtall-airline-16756
12/19/2025, 3:59 AMabundant-magician-17307
12/19/2025, 5:53 AMtall-airline-16756
12/19/2025, 5:54 AMabundant-magician-17307
12/19/2025, 5:57 AMtall-airline-16756
12/19/2025, 5:58 AMabundant-magician-17307
12/19/2025, 5:59 AMtall-airline-16756
12/19/2025, 5:59 AMadvanced 中明确设置 `width`/`height`/`framerate`。
• 使用高质量 codec/profile:`video_codec` 设置为 `H264_HIGH`,并把 bitrate 设为 6000000(单位 bps)。
• 如你的环境要求使用 preset 可替换为 `"preset": "H264_1080P_30"`(示例里采用 explicit advanced)。
请求示例(JSON):
{
"room_name": "<your-room>",
"layout": "speaker",
"advanced": {
"width": 1920,
"height": 1080,
"framerate": 30,
"video_codec": "H264_HIGH",
"video_bitrate": 6000000,
"key_frame_interval": 3
},
"file_outputs": [
{
"file_type": "MP4",
"filepath": "recordings/<your-room>-recording-1080p.mp4"
}
],
"audio_only": false
}
替代更简洁且同样经常验证可用的方式(使用预设):
{
"room_name": "<your-room>",
"layout": "speaker",
"preset": "H264_1080P_30",
"file_outputs": [
{
"file_type": "MP4",
"filepath": "recordings/<your-room>-preset-1080p.mp4"
}
]
}
快速验证建议:
• 提交后用 ffprobe 检查输出文件信息:
ffprobe -v error -select_streams v:0 -show_entries stream=width,height,codec_name,bit_rate,profile -of default=noprint_wrappers=1:nokey=1 exported.mp4 输出应显示 1920 / 1080 和接近 6000000 的 bit_rate 与 H.264 profile。
• 若仍模糊:抓取(1)本地浏览器截图、(2)Egress 生成的 image_outputs 截图(在请求中临时加入 image_outputs width/height=1920/1080),以及(3)导出文件 ffprobe 输出,发给我我来进一步分析。
需要我把这段 JSON 包装成你当前调用接口所需的完整请求体格式(包括任何认证字段或 stream/file_outputs 的 S3 配置)吗?abundant-magician-17307
12/19/2025, 6:07 AMtall-airline-16756
12/19/2025, 6:08 AMpreset / `advanced`(width/height/framerate/video_bitrate/video_codec/key_frame_interval)直接决定后续渲染与编码目标。
• 排查:确认请求 JSON 中分辨率/码率/codec 是否为期望值。
2) 分配与启动 Egress 处理器(worker)
• Egress 服务从队列调度 worker,worker 在节点上准备资源(虚拟显示、PulseAudio、Chromium、GStreamer)。
• 影响点:若节点资源受限(CPU/GPU/内存),编码器可能降质或丢帧。
• 排查:监控 Egress 节点在录制期间的 CPU/内存/负载。
3) 渲染(仅 Room Composite / Web Egress)
• 启动无头 Chromium,在虚拟 X display 上以指定或默认尺寸加载模板页面(内置 template 或 custom_base_url)。页面通过 JS 连接房间并渲染 UI(视频画面、文字、布局)。
• 影响点:Chromium 的实际 viewport/渲染尺寸可能与期待不符(被缩放、deviceScaleFactor 不一致或 template CSS 有缩放),导致源像素不足;Canvas/WebGL/字体未按高 DPI 绘制也会模糊。
• 排查:检查日志中 Chromium 启动时记录的 display dims;本地用 Puppeteer 在相同 viewport/scale 做截图对比;确保 template 的 meta viewport 与根 CSS 固定为目标像素。
4) 捕获(从 Chromium 到编码器)
• GStreamer /采集子系统捕获 Chromium 窗口画面(整帧截图流)并获取系统音频(PulseAudio)。
• 影响点:捕获分辨率必须与 Chromium 渲染一致;若捕获被缩放或截取子区域会丢失细节。
• 排查:通过 image_outputs(1920x1080 截图)或抓取中间帧验证捕获像素是否为期望分辨率。
5) 编码(GStreamer 转码)
• 捕获的像素通过 GStreamer 管道编码为 H.264(或其它 codec),并设置码率、profile、关键帧间隔等。
• 影响点:编码器配置不当(低 profile、错误的单位导致 bitrate 未生效)、或编码器在性能受限时降级,会导致模糊或伪影;有时字段单位差异(bps vs kbps)会导致实际码率远低于预期。
• 排查:对导出文件运行 ffprobe,确认实际 width/height/codec/bit_rate/profile 与请求一致;查看 Egress 日志中 GStreamer 启动与参数信息。
6) 封装与推流(RTMP 输出)
• 编码后流被封装为 RTMP 并推送到指定 RTMP 服务器 URL。可同时推多路。
• 影响点:RTMP 服务器若在入站做转码/下采样或带宽不够,会导致最终观看端或录制文件变差。
• 排查:在 RTMP 服务器端查看实际接收到的流信息(ffprobe 或服务日志),确认没有被二次转码或下采样。
7) 下游/客户端播放(观众端)
• 最终用户或 CDN 播放端可能有自适应或客户端解码问题。
• 排查:直接对比 Egress 生成的本地文件(或从 RTMP 服务器拉取的流)与源截图,确定是 Egress 输出本身模糊还是播放端问题。
快速定位建议(按优先级执行)
• 检查并贴出完整 StartRoomCompositeEgress 请求 JSON(确认 advanced/preset 字段)。
• 用 ffprobe 检查导出文件实际的 width/height/bit_rate/codec/profile。
• 在 Egress 期间启用 image_outputs(1920x1080)或抓取 Chromium 渲染截图,和本地浏览器截图以及导出文件帧做对比。
• 查看 Egress 日志中 Chromium 启动行(display dims)和 GStreamer 参数输出。
• 确认 RTMP 服务器没有对入站流做二次转码或降采样。
总结
• Egress 到 RTMP 的流程:请求 -> worker 启动 -> Chromium 渲染 -> 捕获 -> GStreamer 编码 -> RTMP 推流 -> 下游播放。
• 画质下降常见原因:Chromium 渲染分辨率/viewport 不一致、捕获或编码参数失配、编码器配置/资源限制、RTMP 端二次转码。
把你的请求 JSON、ffprobe 输出和一组截图发来,我会基于这些证据指出具体环节并给出可直接替换的修复配置。
Sources: Universal Egress | Egress examples | LiveKit docs | RoomComposite Egress | LiveKit Docsabundant-magician-17307
12/19/2025, 6:33 AMtall-airline-16756
12/19/2025, 6:34 AMconst canvas = document.querySelector('#myCanvas');
const targetW = 1920;
const targetH = 1080;
const dpr = window.devicePixelRatio || 1;
// 设置内部像素尺寸(物理像素)
canvas.width = targetW * dpr;
canvas.height = targetH * dpr;
// CSS 大小为逻辑像素(不乘 DPR)
canvas.style.width = targetW + 'px';
canvas.style.height = targetH + 'px';
const ctx = canvas.getContext('2d');
// 缩放上下文以适配 DPR(仅当 dpr !== 1 时)
if (dpr !== 1) ctx.scale(dpr, dpr);
说明:确保 Egress 使用 1920x1080 渲染时,canvas 在 Chromium 中以 1:1 物理像素绘制。
2) 确保绘制前字体已加载,避免回退字体导致模糊
• 使用 FontFaceObserver 或等待 document.fonts.ready 再绘制文字:
await document.fonts.ready;
// 然后执行文字绘制
3) 控制图像平滑(按场景)
• 若绘制的为像素化图或经常缩放的图片可禁用平滑:
ctx.imageSmoothingEnabled = false;
ctx.webkitImageSmoothingEnabled = false;
ctx.msImageSmoothingEnabled = false;
• 对于矢量文字通常保持 smoothing=true 更自然。
4) 避免 CSS 或父容器层级上的缩放/transform/zoom
• 检查父元素是否含 `transform: scale(...)`、`zoom` 或非整数 translate,会使 canvas 被再次缩放并模糊。若存在,移除或改为直接调整 canvas 内部像素。
5) 使用整数像素对齐绘制坐标
• 在绘制文字或线条时尽量使用整数坐标(或 0.5 用于1px线),避免半像素渲染导致模糊:
const x = Math.round(desiredX);
const y = Math.round(desiredY);
ctx.fillText(text, x, y);
6) 如果 Canvas 大小需响应式,自适应目标分辨率时动态设置
• 在初始化和窗口尺寸变化时,读取目标渲染尺寸(或 Egress 告知的 width/height),并按步骤 1 动态重设 canvas.width/height 与 ctx.scale。
7) 本地 headless 校验(快速复现)
• 用 Puppeteer 设置与 Egress 相同的 viewport 与 deviceScaleFactor:
await page.setViewport({ width:1920, height:1080, deviceScaleFactor:1 }); 截图比对本地 headless 与 Egress 导出帧,能快速判断是否为 canvas 绘制问题。
我需要你提供(把相关片段贴上来)
• Canvas 初始化与绘制的 JS 代码(设置 width/height、ctx.scale、文字绘制等部分)。
• 与 canvas 相关的 CSS(父容器尺寸、transform/zoom、font-size 使用方式)。
• 如方便,提供一张本地 headless 截图与一张 Egress 导出模糊帧用于对比。
把代码和截图发来后我会直接在你的代码上标注修改点并给出替换后的可运行代码。abundant-magician-17307
12/19/2025, 6:39 AMtall-airline-16756
12/19/2025, 6:39 AM--window-size=1920,1080 --force-device-scale-factor=1 --high-dpi-support=1
• 如果无法修改启动脚本,请把你找到的日志贴上来,我来判断是否需要调整。
3) 在 Canvas 端确保 1:1 物理像素绘制(务必)
• 初始化示例(对照你的目标 1920x1080):
const canvas = document.getElementById('myCanvas');
const targetW = 1920;
const targetH = 1080;
const dpr = window.devicePixelRatio || 1;
canvas.width = targetW * dpr;
canvas.height = targetH * dpr;
canvas.style.width = targetW + 'px';
canvas.style.height = targetH + 'px';
const ctx = canvas.getContext('2d');
if (dpr !== 1) ctx.scale(dpr, dpr);
ctx.imageSmoothingEnabled = true; // 或根据场景禁用
• 重要:在绘制文字前等待字体加载(await document.fonts.ready)。
4) 验证 headless(Xvfb+Chromium)渲染是否为目标像素
• 在 Egress 请求中临时添加一个 `image_outputs`(1920x1080)来抓取 Chromium 渲染的截图;或在本地用 Puppeteer 以相同启动参数(window-size + force-device-scale-factor)做截图。
• 对比:本地浏览器截图 vs headless 截图 vs 导出文件帧。若 headless 截图模糊,问题在渲染/屏幕设置;若 headless 清晰但导出模糊,问题在编码阶段。
5) 检查并避免 CSS/父容器缩放
• 确保 canvas 的父容器和 body 没有 scale/zoom/transform。强制 CSS:
html,body,#root { width:1920px; height:1080px; margin:0; padding:0; overflow:hidden; }
#myCanvas { width:1920px; height:1080px; display:block; }
6) 若仍模糊,抓取更多证据发我
• Egress 启动时的 display dims 日志行(完整行)
• Chromium 启动 args(若日志中有)
• headless screenshot(image_outputs)的一张图
• 你 canvas 初始化与绘制关键代码片段(canvas.width/height、ctx.scale、字体加载代码)
下一步建议(优先)
• 先把第 1 项 Xvfb/Chromium 启动日志贴上来;同时在 Egress 请求里临时开启 `image_outputs`(1920x1080)并把那张截图发给我。我拿到这些就能快速判断是屏幕尺寸/DPR 问题还是 Canvas 绘制本身的问题,并给出具体的容器/Chromium 启动或 canvas 代码修正。abundant-magician-17307
12/19/2025, 6:58 AMtall-airline-16756
12/19/2025, 6:59 AMconst ctx = canvas.getContext('2d');
ctx.imageSmoothingEnabled = false;
ctx.webkitImageSmoothingEnabled = false;
ctx.msImageSmoothingEnabled = false;
ctx.drawImage(videoElem, 0, 0, canvas.width, canvas.height);
2) 确保 Canvas 内部像素与目标捕获分辨率 1:1 对齐
const targetW = 1920, targetH = 1080;
const dpr = window.devicePixelRatio || 1;
canvas.width = targetW * dpr;
canvas.height = targetH * dpr;
canvas.style.width = targetW + 'px';
canvas.style.height = targetH + 'px';
const ctx = canvas.getContext('2d');
if (dpr !== 1) ctx.scale(dpr, dpr);
3) 确保父容器无缩放/transform/zoom
• CSS 强制:
html,body,#root { width:1920px; height:1080px; margin:0; padding:0; overflow:hidden; }
#canvas { width:1920px; height:1080px; display:block; transform:none; }
4) 在 headless 环境强制设备缩放因子与窗口大小
• Chromium 启动参数应包含:
--window-size=1920,1080 --force-device-scale-factor=1 --high-dpi-support=1
• Xvfb 启动应使用: -screen 0 1920x1080x24
5) 在绘制前等待字体加载(避免因回退字体造成模糊)
await document.fonts.ready;
6) 做对比验证(便于定位)
• 在 Egress 请求中临时开启 image_outputs(1920x1080)抓取 headless 渲染截图;同时本地 Puppeteer 用相同启动参数截图。对比三张图(本地浏览器 / headless 截图 / 导出文件帧)。
需要我直接帮你修改的地方
• 若你使用自定义模板,请把 Canvas 初始化&绘制相关代码片段和关键 CSS 发来,我会直接标注修改并给出可替换代码。
• 若你希望我验证 headless 启动参数或建议如何在容器中添加 Chromium/Xvfb 参数,也把 Egress 日志中 Chromium 启动那行(包含 args 和 display dims)发来。
你想先我帮你:A) 给出修改后的模板代码片段,或 B) 检查你现有的 Canvas 代码与 Xvfb/Chromium 启动日志?
Sources: RoomComposite Egress | LiveKit Docs | Yeah, but can it run DOOM? | Universal Egress | Composite and web recordings | LiveKit docs | client-sdk-unity-web | livekit/livekit_composite | DeepWikiabundant-magician-17307
12/19/2025, 7:46 AMtall-airline-16756
12/19/2025, 7:47 AMsrc 或 static 目录(一般是 React/JSX 或静态 JS 文件)。
◦ 在源码中搜索 `canvas`、`getContext('2d')`、`drawImage`、或 `imageSmoothingEnabled`,找到绘制屏幕/视频到 canvas 的代码位置。
◦ 按前面建议修改:禁用平滑、按 DPR 设置 canvas.width/height、ctx.scale(dpr,dpr)、等待 `document.fonts.ready`。
◦ 构建并把生成的静态文件部署到可访问 URL,调用 Egress 时把该 URL 作为 `custom_base_url`(或替换容器中内置模板)。
2) 如果你在用自定义模板(custom_base_url)
• Canvas 代码就在你自己的模板静态文件里(通常是 static/js/*.js 或项目的源文件如 `src/...`)。
• 快速定位方法:在模板项目中全局搜索关键字 `canvas`、`getContext('2d')`、`drawImage`、`imageSmoothingEnabled`、或 `devicePixelRatio`。
• 常见文件位置:
◦ 单页面应用 (Vue/React):在渲染组件(如 RecordingView、CompositeCanvas、Renderer)里。
◦ 纯静态:在某个 main.js 或 render.js 里初始化并绘制 canvas。
• 直接修改(最小改动,复制到你的代码):
const canvas = document.getElementById('yourCanvasId');
const targetW = 1920;
const targetH = 1080;
const dpr = window.devicePixelRatio || 1;
canvas.width = targetW * dpr;
canvas.height = targetH * dpr;
canvas.style.width = targetW + 'px';
canvas.style.height = targetH + 'px';
const ctx = canvas.getContext('2d');
ctx.imageSmoothingEnabled = false;
if (dpr !== 1) ctx.scale(dpr, dpr);
await document.fonts.ready;
ctx.drawImage(videoElem, 0, 0, targetW, targetH);
• 部署修改后的模板并在 StartRoomCompositeEgress 请求中设置 custom_base_url 指向你的模板 URL。
3) 如果你不确定或想我直接改
• 把你的模板源码里与 canvas 相关的 JS 文件片段贴上来(初始化 canvas 的那部分和 drawImage/文字绘制代码),或告诉我你是在使用官方模板还是 custom。
• 我会直接在你的代码片段上标注修改并给出可以直接替换的代码。
想先把哪个文件或代码片段贴上来?abundant-magician-17307
12/19/2025, 8:06 AMtall-airline-16756
12/19/2025, 8:07 AMabundant-magician-17307
12/19/2025, 8:09 AMabundant-magician-17307
12/22/2025, 5:47 AM