From 5e135dd69021509406a438aaab8898135a8bfcf1 Mon Sep 17 00:00:00 2001 From: wudechang Date: Thu, 4 Jul 2024 14:32:18 +0800 Subject: [PATCH] feat: hls daterange tag support --- fixtures/hls/index.js | 6 ++ .../src/hls/manifest-loader/parser/media.js | 23 +++++++- .../src/hls/manifest-loader/parser/model.js | 2 + .../src/hls/manifest-loader/parser/utils.js | 55 +++++++++++++++++++ 4 files changed, 85 insertions(+), 1 deletion(-) diff --git a/fixtures/hls/index.js b/fixtures/hls/index.js index e32c1814..8f42c8c9 100644 --- a/fixtures/hls/index.js +++ b/fixtures/hls/index.js @@ -161,6 +161,12 @@ window.onload = function () { player.on('retry', function (event) { pushEvent('retry', event) }) player.on('core_event', function (event) { pushEvent(event.eventName, event) }) player.on('error', function (event) { pushEvent(event.errorType, event, dlError) }) + player.on('core_event', (info) => { + const { eventName, playlist } = info + if (eventName === 'core.hlslevelloaded' && playlist) { + console.log('playlist1111', playlist) + } + }) dStreamForce.checked = true function refreshStreams() { diff --git a/packages/xgplayer-hls/src/hls/manifest-loader/parser/media.js b/packages/xgplayer-hls/src/hls/manifest-loader/parser/media.js index 2664e619..32efb70c 100644 --- a/packages/xgplayer-hls/src/hls/manifest-loader/parser/media.js +++ b/packages/xgplayer-hls/src/hls/manifest-loader/parser/media.js @@ -1,5 +1,5 @@ import { MediaPlaylist, MediaSegment, MediaSegmentKey } from './model' -import { getAbsoluteUrl, parseAttr, parseTag } from './utils' +import { getAbsoluteUrl, parseAttr, parseTag, isValidDaterange } from './utils' export function parseMediaPlaylist (lines, parentUrl, useLowLatency) { const media = new MediaPlaylist() @@ -156,6 +156,27 @@ export function parseMediaPlaylist (lines, parentUrl, useLowLatency) { curSegment = new MediaSegment(parentUrl) } break + case 'SKIP': { + const attr = parseAttr(data) + const skippedSegments = parseInt(attr['SKIPPED-SEGMENTS'], 10) + if (skippedSegments <= Number.MAX_SAFE_INTEGER) { + media.skippedSegments += skippedSegments + } + } + break + case 'DATERANGE': { + const attr = parseAttr(data) + const dateRangeWithSameId = media.dateRanges[attr.ID] + attr._startDate = dateRangeWithSameId ? dateRangeWithSameId._startDate : new Date(attr['START-DATE']) + const endDate = dateRangeWithSameId?._endDate || new Date(attr.END_DATE) + if (Number.isFinite(endDate)) { + attr._endDate = endDate + } + if (isValidDaterange(attr, dateRangeWithSameId) || media.skippedSegments) { + media.dateRanges[attr.ID] = attr + } + } + break default: } } diff --git a/packages/xgplayer-hls/src/hls/manifest-loader/parser/model.js b/packages/xgplayer-hls/src/hls/manifest-loader/parser/model.js index 11569293..79a1a805 100644 --- a/packages/xgplayer-hls/src/hls/manifest-loader/parser/model.js +++ b/packages/xgplayer-hls/src/hls/manifest-loader/parser/model.js @@ -110,6 +110,8 @@ export class MediaPlaylist { endPartIndex = 0 /** @type {Array.} */ segments = [] + dateRanges = {} + skippedSegments = 0 } export class MediaSegment { diff --git a/packages/xgplayer-hls/src/hls/manifest-loader/parser/utils.js b/packages/xgplayer-hls/src/hls/manifest-loader/parser/utils.js index 41d83042..92530114 100644 --- a/packages/xgplayer-hls/src/hls/manifest-loader/parser/utils.js +++ b/packages/xgplayer-hls/src/hls/manifest-loader/parser/utils.js @@ -51,3 +51,58 @@ export function getCodecs (type, codecs) { } } } + +export function isValidDaterange(attr, dateRangeWithSameId) { + let _badValueForSameId + if (dateRangeWithSameId) { + for (const key in dateRangeWithSameId) { + if ( + Object.prototype.hasOwnProperty.call(dateRangeWithSameId, key) && + attr[key] !== dateRangeWithSameId[key] + ) { + _badValueForSameId = key; + break; + } + } + } + + let duration = null + if (attr.DURATION) { + duration = parseFloat(attr.DURATION) + if (!Number.isFinite(duration)) { + duration = null + } else if (attr._endDate) { + duration = (attr._endDate.getTime() - attr._startDate.getTime()) / 1000 + } + } + + const cue = enumeratedStringList(attr.CUE || attr['X-CUE'], { + pre: false, + post: false, + once: false, + }) + + return ( + !!attr.ID && + !_badValueForSameId && + Number.isFinite(attr._startDate.getTime()) && + (duration === null || duration >= 0) && + (!(attr.END_ON_NEXT === 'YES') || !!attr.CLASS) && + (!attr.CUE || + (!cue.pre && !cue.post) || + cue.pre !== cue.post) && + (!(attr.CLASS === 'com.apple.hls.interstitial') || + 'X-ASSET-URI' in attr || + 'X-ASSET-LIST' in attr) + ); +} + +function enumeratedStringList(attrValue, dict) { + return (attrValue ? attrValue.split(/[ ,]+/) : []).reduce( + (result, identifier) => { + result[identifier.toLowerCase()] = true; + return result; + }, + dict, + ); +}