监听HLS ID3元数据时cuechange事件在TextTrack更新前多次触发
HLS ID3元数据
cuechange事件触发异常修复方案 开发HTTP Live Streaming相关功能时,需要通过Javascript监听流中嵌入的ID3格式定时元数据,最初的实现代码如下:
videoElement.textTracks.addEventListener("addtrack", onAddTrack); const onAddTrack = function (event) { const track = event.track; if (track.kind === "metadata") { track.addEventListener("cuechange", () => { // 处理cue变更事件 // 提取生效的cue,解析后展示对应内容 }); } }
根据MDN对cuechange事件的定义:
"The
cuechangeevent fires when aTextTrackhas changed the currently displaying cues."
释义:cuechange事件会在TextTrack修改当前显示的cue时触发
但实际运行时发现cuechange事件在TextTrack完成当前显示cue的修改之前就被多次调用,不符合预期。
问题原因
该问题是HLS元数据轨道的解析特性导致的:浏览器解析HLS流中的ID3元数据时,会批量将解析到的cue入队,每入队一个cue就可能触发一次cuechange事件,此时activeCues还未完成最终更新,就会出现多次触发、提前触发的问题。
修复方案
- 每次
cuechange触发时,优先读取track.activeCues集合,过滤空值和重复的cue后再处理,不要依赖事件触发时机判断更新是否完成 - 增加已处理cue的缓存逻辑,同一个cue的ID或者内容如果已经处理过就跳过,避免重复执行业务逻辑
- 针对HLS的ID3元数据场景,可以直接从cue的value中读取ID3原始数据,不需要等待事件回调的额外参数
修复后代码示例:
// 缓存已处理的cue id,避免重复处理 const processedCueIds = new Set(); videoElement.textTracks.addEventListener("addtrack", onAddTrack); const onAddTrack = function (event) { const track = event.track; if (track.kind === "metadata") { track.addEventListener("cuechange", () => { // 直接读取当前生效的cue集合 const activeCues = track.activeCues; if (!activeCues || activeCues.length === 0) return; // 遍历所有生效cue处理 for (let i = 0; i < activeCues.length; i++) { const cue = activeCues[i]; // 跳过已处理的cue if (processedCueIds.has(cue.id)) continue; processedCueIds.add(cue.id); // 解析ID3元数据,执行业务逻辑 // 示例:输出ID3的TXXX字段内容 if (cue.value) { cue.value.forEach(dataFrame => { if (dataFrame.key === 'TXXX') { console.log('ID3元数据内容:', dataFrame.data); } }); } } }); } }
内容的提问来源于stack exchange,提问作者Ashutosh
相关产品推荐
相关产品推荐

