From 5cdcbea67d190ca18efb1a4a86a4a1b5ae66764b Mon Sep 17 00:00:00 2001 From: Alex Manaev Date: Mon, 1 Dec 2025 22:03:44 +0300 Subject: [PATCH] =?UTF-8?q?#68064=20=D0=90=D0=B1=D1=81=D0=BE=D0=BB=D1=8E?= =?UTF-8?q?=D1=82=D0=BD=D1=8B=D0=B5=20=D0=B7=D0=BD=D0=B0=D1=87=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D1=8F=20=D0=B2=20hls=20live?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/internal/drm/dash/VokaDash.ts | 31 ++++--- src/internal/player/VokaCorePlayer.ts | 84 +++++++++++++------ src/internal/player/native/VokaTech.ts | 33 ++++++-- .../sourcehandler/VokaAppleSourceHandler.ts | 2 +- .../VokaFairplaySourceHandler.ts | 2 +- .../player/native/apple/tech/VokaAppleTech.ts | 24 +++++- .../sourcehandler/VokaDashSourceHandler.ts | 2 +- .../player/native/dash/tech/VokaDashTech.ts | 27 +++--- .../native/hls/processors/HlsProcessor.ts | 68 +++++++-------- .../hls/sourcehandler/VokaHlsSourceHandler.ts | 2 +- .../player/native/hls/tech/VokaHlsTech.ts | 9 +- .../mp4/sourcehandler/VokaMp4SourceHandler.ts | 2 +- src/internal/player/native/setup-sourceset.ts | 2 +- .../sourcehandler/VokaTizenSourceHandler.ts | 2 +- .../player/native/tizen/tech/VokaTizenTech.ts | 13 +++ .../sourcehandler/VokaWebOSSourceHandler.ts | 2 +- .../player/native/webos/tech/VokaWebOSTech.ts | 13 +++ src/public/VokaPlayerImpl.ts | 2 +- 18 files changed, 210 insertions(+), 110 deletions(-) diff --git a/src/internal/drm/dash/VokaDash.ts b/src/internal/drm/dash/VokaDash.ts index 47fe594..5657a2b 100644 --- a/src/internal/drm/dash/VokaDash.ts +++ b/src/internal/drm/dash/VokaDash.ts @@ -178,6 +178,7 @@ export const DashErrorTypeIterable = { ...DashPlaybackErrorType, ...DashDrmErrorType } +export type DashProgramDateTimeAPI = Pick export interface VokaDashOptions extends VokaOptionsType { timeout: { chunk: number @@ -606,25 +607,23 @@ export default class VokaDash { return (this.mediaPlayer as MediaPlayerClass).duration() } - getAbsoluteRange(): any | null { - if (!this.mediaPlayer) return null - - const range = { start: 0, end: 0 } - if (!this.mediaPlayer.isDynamic()) { - range.end = this.mediaPlayer.duration() - return range + /** + * Абсолютное время (секунды) для DASH на основе availabilityStartTime (AST). + */ + getProgramDateTimeOffset(): number { + if (!this.mediaPlayer || !this.mediaPlayer.isDynamic()) { + return 0 } - const dashMetrics = this.mediaPlayer?.getDashMetrics() - if (!dashMetrics) return null + if (!dashMetrics) return 0 try { - const dvrInfo = dashMetrics.getCurrentDVRInfo('video') - range.start = dvrInfo.manifestInfo.availableFrom.getTime() / 1000 + dvrInfo.range.start - range.end = this.mediaPlayer?.timeAsUTC() || 0 - return range - } catch { - return 0 - } + const dvrInfo = dashMetrics.getCurrentDVRInfo('video') + const availableFrom = dvrInfo?.manifestInfo?.availableFrom + if (availableFrom && typeof availableFrom.getTime === 'function') { + return availableFrom.getTime() / 1000 + } + } catch {} + return 0 } getTimeshiftAvailable() { diff --git a/src/internal/player/VokaCorePlayer.ts b/src/internal/player/VokaCorePlayer.ts index 4c46218..6c16cb3 100755 --- a/src/internal/player/VokaCorePlayer.ts +++ b/src/internal/player/VokaCorePlayer.ts @@ -1,8 +1,8 @@ import chromecastPlugin from '@silvermine/videojs-chromecast/' import { Promise } from 'es6-promise' import { EventBus } from 'ts-bus' +import type { VideoJsPlayer } from 'video.js' import videojs from 'video.js' -import type Player from 'video.js/dist/types/player' import VokaEvent from '@/constants/VokaEvent' import VokaBusEvent from '@/internal/events/VokaBusEvent' @@ -28,6 +28,7 @@ import type { VokaOptions } from '@/public/@types' import type { ITimeRange } from '@/public/IVokaPlayer' import type { Quality } from '@/public/models/ILoadOptions' import { VokaContentType } from '@/public/models/VokaContentType' +import type { VideoJsPlayer } from 'video.js' import '@/components/PosterImage' import '@/components/Skin' @@ -39,10 +40,10 @@ import '@/plugins/VokaLogPlugin' import '@/plugins/VokaMagicRemotePlugin' import '@/plugins/VokaMetricsPlugin' -const log = videojs.log.createLogger('[VokaCorePlayer]') as typeof videojs.log + +const log = videojs.log.createLogger('[VokaCorePlayer]') namespace VokaCorePlayer { - type VideoJsPlayerOptions = Parameters[1] type PlayerReadyHandler = (player: CorePlayer) => void const playbackRates = [ 0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2 ] @@ -115,7 +116,7 @@ namespace VokaCorePlayer { export class CorePlayer { private readonly bus: EventBus - private player!: Player + private player!: VideoJsPlayer private autoPlaySupported: boolean private autoPlayOption: boolean | 'muted' | any private readonly stateEmitter: EventBus @@ -204,25 +205,28 @@ namespace VokaCorePlayer { } public getAbsoluteRange(): ITimeRange | null { - if (this.player.liveTracker) { + // Для live возвращаем абсолютный диапазон; для VOD — 0/длительность + const d = this.player.duration() + if (!isFinite(d)) { + const lt = this.player.liveTracker + const startRel = lt ? lt.seekableStart() : 0 + const endRel = lt ? lt.seekableEnd() : 0 + const tech = this.player.tech({ safeUsage: true }) as VokaTech.IVokaBaseTech + const offset: number = tech.getProgramDateTimeOffset() return { - start: this.player.liveTracker.seekableStart(), - end: this.player.liveTracker.seekableEnd() + start: offset + startRel, + end: offset + endRel } } return { start: 0, - end: this.player.duration() || 0 + end: d || 0 } } public getTimeshiftAvailable(): boolean { - const tech = this.player.tech({ safeUsage: true }) as VokaTech.Tech - // This require because Tizen and WebOS do not inherit from VokaTech - if (typeof tech['getTimeshiftAvailable'] === 'function') { - return tech.getTimeshiftAvailable() - } - return false + const tech = this.player.tech({ safeUsage: true }) as VokaTech.IVokaBaseTech + return tech.getTimeshiftAvailable() } public get isPaused(): boolean { @@ -255,26 +259,44 @@ namespace VokaCorePlayer { public seek(seconds: number): void { log('[seek()] called with seconds', seconds) - if (Math.abs(this.currentTime - seconds) < 0.5 || !isFinite(seconds)) { + + // Прыжок к live edge по соглашению: Infinity + if (seconds === Infinity || seconds === Number.POSITIVE_INFINITY) { + const lt = this.player.liveTracker + if (lt && this.isLive) { + try { lt.seekToLiveEdge() } catch {} + } + // Для Infinity больше ничего не делаем + return + } + + // Избегаем микроскачков + if (Math.abs(this.currentTime - seconds) < 0.5) { log('[seek()] Skip step less than 0.5 sec') return } - const range = this.getAbsoluteRange() - const start = range?.start ?? 0 - const end = range?.end || Infinity - - if (seconds < start) { - seconds = start - log('[seek()] reset seconds to start value', seconds, start) - } else if (seconds >= end) { - seconds = end - 0.5 - log('[seek()] reset seconds under end value', seconds, end) + // Кламп по доступному seekable/длительности + const tr = this.player.seekable() + let target = seconds + if (tr && tr.length) { + const start = tr.start(0) + const end = tr.end(tr.length - 1) + // небольшой отступ от конца окна + const max = isFinite(end) ? Math.max(start, end - 0.5) : end + if (target < start) target = start + else if (isFinite(max) && target > max) target = max + } else { + const d = this.player.duration() || 0 + if (target < 0) target = 0 + if (isFinite(d) && target > d) target = Math.max(0, d - 0.5) } - this.player.currentTime(seconds) + this.player.currentTime(target) } + // --- + public mute(on: boolean) { this.player.muted(on) } @@ -306,6 +328,16 @@ namespace VokaCorePlayer { return !isFinite(duration) } + /** + * Абсолютное текущее время (секунды Unix) для live. + */ + public getAbsoluteCurrentTime(): number | null { + if (!this.isLive) return this.player.currentTime() || 0 + const tech = this.player.tech({ safeUsage: true }) as VokaTech.IVokaBaseTech + const offset: number = tech.getProgramDateTimeOffset() + return offset + (this.player.currentTime() || 0) + } + public async load(content: IContent): Promise { if (!content.url) { throw new Error('Empty content URL') diff --git a/src/internal/player/native/VokaTech.ts b/src/internal/player/native/VokaTech.ts index be5f6ca..bc8bf20 100644 --- a/src/internal/player/native/VokaTech.ts +++ b/src/internal/player/native/VokaTech.ts @@ -1,9 +1,10 @@ -import VokaEvent from '@/constants/VokaEvent' -import { IVokaSource } from '@/internal/player/native/VokaSourceHandler' -import { silencePromise } from '@/internal/utils/promise' import window from 'global' import videojs from 'video.js' +import VokaEvent from '@/constants/VokaEvent' +import type { IVokaSource } from '@/internal/player/native/VokaSourceHandler' +import { silencePromise } from '@/internal/utils/promise' + import setupSourceset from './setup-sourceset' const browser = videojs.browser @@ -15,12 +16,22 @@ namespace VokaTech { const VideoTech = videojs.getTech('Tech') - export interface ITimeRange { - start: number - end: number + /** + * Базовый интерфейс для поддержки абсолютного времени. + */ + export interface IVokaBaseTech { + /** + * Абсолютное время (секунды), такое что absolute = offset + relative. + * Для VOD — 0; для LIVE — зависит от технологии (HLS/DASH/нативный). + */ + getProgramDateTimeOffset(): number + /** + * Доступен ли таймшифт для текущего контента. + */ + getTimeshiftAvailable(): boolean } - export class Tech extends VideoTech { + export class Tech extends VideoTech implements IVokaBaseTech { private rememberedVideoTag: Element | null private parentNode: Element | null @@ -770,8 +781,12 @@ namespace VokaTech { return this.el().offsetHeight } - getAbsoluteRange(): ITimeRange | null { - return null + /** + * Абсолютное время (секунды), такое что absolute = offset + relative. + * По умолчанию поддержка отсутствует — возвращаем 0. + */ + getProgramDateTimeOffset(): number { + return 0 } getTimeshiftAvailable(): boolean { diff --git a/src/internal/player/native/apple/sourcehandler/VokaAppleSourceHandler.ts b/src/internal/player/native/apple/sourcehandler/VokaAppleSourceHandler.ts index 2c073c5..b2a45d3 100644 --- a/src/internal/player/native/apple/sourcehandler/VokaAppleSourceHandler.ts +++ b/src/internal/player/native/apple/sourcehandler/VokaAppleSourceHandler.ts @@ -56,4 +56,4 @@ export default class VokaAppleSourceHandler extends VokaSourceHandler { } dispose() {} -} \ No newline at end of file +} diff --git a/src/internal/player/native/apple/sourcehandler/VokaFairplaySourceHandler.ts b/src/internal/player/native/apple/sourcehandler/VokaFairplaySourceHandler.ts index da9f05e..c194000 100644 --- a/src/internal/player/native/apple/sourcehandler/VokaFairplaySourceHandler.ts +++ b/src/internal/player/native/apple/sourcehandler/VokaFairplaySourceHandler.ts @@ -40,4 +40,4 @@ export default class VokaFairplaySourceHandler extends VokaSourceHandler { } dispose() {} -} \ No newline at end of file +} diff --git a/src/internal/player/native/apple/tech/VokaAppleTech.ts b/src/internal/player/native/apple/tech/VokaAppleTech.ts index 91cd86a..fbd8556 100644 --- a/src/internal/player/native/apple/tech/VokaAppleTech.ts +++ b/src/internal/player/native/apple/tech/VokaAppleTech.ts @@ -1,8 +1,11 @@ import videojs from 'video.js' + import VokaTech from '@/internal/player/native/VokaTech' -import VokaFairplaySourceHandler from '../sourcehandler/VokaFairplaySourceHandler' + +import type { IVokaSource} from '../../VokaSourceHandler' +import { VokaSourceHandler } from '../../VokaSourceHandler' import VokaAppleSourceHandler from '../sourcehandler/VokaAppleSourceHandler' -import { IVokaSource, VokaSourceHandler } from '../../VokaSourceHandler' +import VokaFairplaySourceHandler from '../sourcehandler/VokaFairplaySourceHandler' const Browser = videojs.browser const Dom = videojs.dom @@ -236,6 +239,21 @@ class VokaAppleTech extends VokaTech.Tech { static get featuresNativeAudioTracks() { return VokaAppleTech.supportsNativeAudioTracks() } + + seekable() { + return this.el().seekable || videojs.createTimeRanges([]) + } + + /** + * Абсолютное время (секунды) для нативного HLS: предполагаем seekableEnd ≈ now. + */ + getProgramDateTimeOffset(): number { + const seekable: TimeRanges = this.seekable() as any + if (!seekable || seekable.length < 1) return 0 + const endRel = seekable.end(seekable.length - 1) + if (!isFinite(endRel)) return 0 + return (Date.now() / 1000) - endRel + } } VokaTech.Tech.withSourceHandlers(VokaAppleTech) @@ -244,4 +262,4 @@ VokaAppleTech.registerSourceHandler(new VokaFairplaySourceHandler()) // FairPlay VokaAppleTech.registerSourceHandler(new VokaAppleSourceHandler()) VokaAppleTech.registerTech('VokaAppleTech', VokaAppleTech) -export default VokaAppleTech \ No newline at end of file +export default VokaAppleTech diff --git a/src/internal/player/native/dash/sourcehandler/VokaDashSourceHandler.ts b/src/internal/player/native/dash/sourcehandler/VokaDashSourceHandler.ts index 11da0df..9313b09 100644 --- a/src/internal/player/native/dash/sourcehandler/VokaDashSourceHandler.ts +++ b/src/internal/player/native/dash/sourcehandler/VokaDashSourceHandler.ts @@ -56,4 +56,4 @@ export default class VokaDashSourceHandler extends VokaSourceHandler { } dispose() {} -} \ No newline at end of file +} diff --git a/src/internal/player/native/dash/tech/VokaDashTech.ts b/src/internal/player/native/dash/tech/VokaDashTech.ts index db3156f..3f87df7 100644 --- a/src/internal/player/native/dash/tech/VokaDashTech.ts +++ b/src/internal/player/native/dash/tech/VokaDashTech.ts @@ -1,10 +1,12 @@ import videojs from "video.js" -import VokaDashSourceHandler from '../sourcehandler/VokaDashSourceHandler' -import PlatformCapabilities from '@/internal/utils/PlatformCapabilities' -import { DashContext } from '../processors/DashContext' + +import type { DashErrorType, DashProgramDateTimeAPI } from '@/internal/drm/dash/VokaDash' +import type { IVokaSource } from '@/internal/player/native/VokaSourceHandler' import VokaTech from '@/internal/player/native/VokaTech' -import { DashErrorType } from '@/internal/drm/dash/VokaDash' -import { IVokaSource } from '@/internal/player/native/VokaSourceHandler' +import PlatformCapabilities from '@/internal/utils/PlatformCapabilities' + +import { DashContext } from '../processors/DashContext' +import VokaDashSourceHandler from '../sourcehandler/VokaDashSourceHandler' const Browser = videojs.browser @@ -65,12 +67,17 @@ class VokaDashTech extends VokaTech.Tech { this.sourceHandler_.switchLevel(level) } - getAbsoluteRange() { - return this.sourceHandler_.getAbsoluteRange() + getTimeshiftAvailable() { + const sh = this.sourceHandler_ as DashProgramDateTimeAPI | null + return sh ? sh.getTimeshiftAvailable() : false } - getTimeshiftAvailable() { - return this.sourceHandler_.getTimeshiftAvailable() + /** + * Абсолютное время для DASH (секунды). + */ + getProgramDateTimeOffset(): number { + const sh = this.sourceHandler_ as DashProgramDateTimeAPI | null + try { return sh?.getProgramDateTimeOffset?.() ?? 0 } catch { return 0 } } /* Static Methods ------------------------------------------------ */ @@ -332,4 +339,4 @@ VokaTech.Tech.withSourceHandlers(VokaDashTech) VokaDashTech.registerSourceHandler(new VokaDashSourceHandler()) VokaTech.Tech.registerTech('VokaDashTech', VokaDashTech) -export default VokaDashTech \ No newline at end of file +export default VokaDashTech diff --git a/src/internal/player/native/hls/processors/HlsProcessor.ts b/src/internal/player/native/hls/processors/HlsProcessor.ts index 543f921..a60ef38 100644 --- a/src/internal/player/native/hls/processors/HlsProcessor.ts +++ b/src/internal/player/native/hls/processors/HlsProcessor.ts @@ -1,4 +1,4 @@ -import Hls, { +import type { ErrorData, Events, FragLoadedData, @@ -10,22 +10,25 @@ import Hls, { ManifestParsedData, MediaPlaylist, } from 'hls.js' -import Player from 'video.js/dist/types/player' -import { trigger } from '@/internal/utils/events' +import Hls from 'hls.js' +import type { EventBus } from 'ts-bus' +import type Player from 'video.js/dist/types/player' + import VokaEvent from '@/constants/VokaEvent' -import HlsProcessorContext from './HlsProcessorContext' -import { wait } from '@/monads/Monoids' -import Future from '@/monads/Future' -import { UrlUtilities } from '@/internal/utils/UrlUtilities' -import HlsLoadContext from './HlsLoadContext' import VokaBusEvent from '@/internal/events/VokaBusEvent' -import { VokaError } from '@/public/models/VokaError' -import { Quality } from '@/public/models/ILoadOptions' -import { IHlsErrorHandler } from './error-handlers/BaseHandler' -import { LevelErrorHandler } from './error-handlers/LevelErrorHandler' -import { ErrorHandler } from './error-handlers/ErrorHandler' +import { trigger } from '@/internal/utils/events' +import { UrlUtilities } from '@/internal/utils/UrlUtilities' +import type Future from '@/monads/Future' +import { wait } from '@/monads/Monoids' +import type { Quality } from '@/public/models/ILoadOptions' import { QualityMapper } from '@/public/models/QualityMapper' -import { EventBus } from 'ts-bus' +import { VokaError } from '@/public/models/VokaError' + +import type { IHlsErrorHandler } from './error-handlers/BaseHandler' +import { ErrorHandler } from './error-handlers/ErrorHandler' +import { LevelErrorHandler } from './error-handlers/LevelErrorHandler' +import HlsLoadContext from './HlsLoadContext' +import HlsProcessorContext from './HlsProcessorContext' export const LIVE_SYNC_DURATION = 10 export const HLS_LEVEL_AUTO = -1 @@ -322,7 +325,7 @@ export default class HlsProcessor { ({ payload }) => { const newId = payload.audioTrackId if (!this.hls || typeof newId !== 'number' ) return - this.hls.audioTrack = newId; + this.hls.audioTrack = newId } ) @@ -335,7 +338,7 @@ export default class HlsProcessor { ({ payload }) => { const newId = payload.trackId if (!this.hls || typeof newId !== 'number' ) return - this.hls.subtitleTrack = newId; + this.hls.subtitleTrack = newId } ) @@ -482,24 +485,21 @@ export default class HlsProcessor { return isFinite(timestamp) ? timestamp - curr : NaN } - getAbsoluteRange() { - if (!this.isLive) { - return { - start: 0, - end: this.player.duration() - } - } - - const start = this.getVideoStartDate() ?? this.getFakeVideoStartDate() - if (!isFinite(start)) return null - - const seekableRange = this.getVideoSeekableRange() - if (!seekableRange) return null - - return { - start: seekableRange.start + start, - end: seekableRange.end + start + /** + * Абсолютное время (секунды) для получения абсолютного времени по HLS. + * При наличии PDT используем его; иначе резерв через конец окна (seekableEnd ~ now). + */ + getProgramDateTimeOffset(): number { + if (!this.isLive) return 0 + + const dt = this.getVideoStartDate() + if (isFinite(dt as number)) { + return dt as number } + const range = this.getVideoSeekableRange() + if (!range) return 0 + const off = (Date.now() / 1000) - range.end + return isFinite(off) ? off : 0 } getTimeshiftAvailable() { @@ -512,7 +512,7 @@ export default class HlsProcessor { return false } - return true; + return true } reload(startSeconds = null) { diff --git a/src/internal/player/native/hls/sourcehandler/VokaHlsSourceHandler.ts b/src/internal/player/native/hls/sourcehandler/VokaHlsSourceHandler.ts index 92faf80..48058d7 100644 --- a/src/internal/player/native/hls/sourcehandler/VokaHlsSourceHandler.ts +++ b/src/internal/player/native/hls/sourcehandler/VokaHlsSourceHandler.ts @@ -59,4 +59,4 @@ export default class VokaHlsSourceHandler extends VokaSourceHandler { } dispose() {} -} \ No newline at end of file +} diff --git a/src/internal/player/native/hls/tech/VokaHlsTech.ts b/src/internal/player/native/hls/tech/VokaHlsTech.ts index b379b53..2743c90 100644 --- a/src/internal/player/native/hls/tech/VokaHlsTech.ts +++ b/src/internal/player/native/hls/tech/VokaHlsTech.ts @@ -1,4 +1,3 @@ -import { IContextUpdated } from '@/public/@types' import Hls from 'hls.js' import type { Level, MediaPlaylist } from 'hls.js/lib/types/level' import type { EventBus } from 'ts-bus' @@ -14,6 +13,7 @@ import { on, trigger } from '@/internal/utils/events' import * as Fn from '@/internal/utils/fn' import PlatformCapabilities from '@/internal/utils/PlatformCapabilities' import { wait } from '@/monads/Monoids' +import type { IContextUpdated } from '@/public/@types' import { Environment } from '@/public/models/Environment' import { QualityLabelVariant } from '@/public/models/ILoadOptions' import { Quality } from '@/public/models/ILoadOptions' @@ -312,8 +312,11 @@ class VokaHlsTech extends VokaTech.Tech { this.hlsProcessor.switchLevel(level) } - getAbsoluteRange() { - return this.hlsProcessor.getAbsoluteRange() + /** + * Абсолютное время для HLS (в секундах). + */ + getProgramDateTimeOffset(): number { + return this.hlsProcessor.getProgramDateTimeOffset() } getTimeshiftAvailable() { diff --git a/src/internal/player/native/mp4/sourcehandler/VokaMp4SourceHandler.ts b/src/internal/player/native/mp4/sourcehandler/VokaMp4SourceHandler.ts index 211e3cb..5ed1300 100644 --- a/src/internal/player/native/mp4/sourcehandler/VokaMp4SourceHandler.ts +++ b/src/internal/player/native/mp4/sourcehandler/VokaMp4SourceHandler.ts @@ -38,4 +38,4 @@ export default class VokaMp4SourceHandler extends VokaSourceHandler { } dispose() {} -} \ No newline at end of file +} diff --git a/src/internal/player/native/setup-sourceset.ts b/src/internal/player/native/setup-sourceset.ts index 93d0516..d26ab99 100644 --- a/src/internal/player/native/setup-sourceset.ts +++ b/src/internal/player/native/setup-sourceset.ts @@ -225,4 +225,4 @@ const setupSourceset = function(tech) { } } -export default setupSourceset \ No newline at end of file +export default setupSourceset diff --git a/src/internal/player/native/tizen/sourcehandler/VokaTizenSourceHandler.ts b/src/internal/player/native/tizen/sourcehandler/VokaTizenSourceHandler.ts index 85ac09e..116e705 100755 --- a/src/internal/player/native/tizen/sourcehandler/VokaTizenSourceHandler.ts +++ b/src/internal/player/native/tizen/sourcehandler/VokaTizenSourceHandler.ts @@ -51,4 +51,4 @@ export default class VokaTizenSourceHandler extends VokaSourceHandler { } dispose() {} -} \ No newline at end of file +} diff --git a/src/internal/player/native/tizen/tech/VokaTizenTech.ts b/src/internal/player/native/tizen/tech/VokaTizenTech.ts index a65d43e..b0349a4 100755 --- a/src/internal/player/native/tizen/tech/VokaTizenTech.ts +++ b/src/internal/player/native/tizen/tech/VokaTizenTech.ts @@ -672,6 +672,19 @@ class VokaTizenTech extends Tech { static get featuresNativeAudioTracks() { return false } + + /** + * Абсолютное время для AVPlay недоступно — возвращаем offset=0. + */ + getProgramDateTimeOffset(): number { + return 0 + } + + // Абсолютный диапазон не используем на уровне tech. + + getTimeshiftAvailable(): boolean { + return false + } } VokaTizenTech.withSourceHandlers(VokaTizenTech) diff --git a/src/internal/player/native/webos/sourcehandler/VokaWebOSSourceHandler.ts b/src/internal/player/native/webos/sourcehandler/VokaWebOSSourceHandler.ts index 29109fc..cf7cddb 100644 --- a/src/internal/player/native/webos/sourcehandler/VokaWebOSSourceHandler.ts +++ b/src/internal/player/native/webos/sourcehandler/VokaWebOSSourceHandler.ts @@ -115,4 +115,4 @@ export default class VokaWebOSSourceHandler extends VokaSourceHandler { ) return nativeDRM } -} \ No newline at end of file +} diff --git a/src/internal/player/native/webos/tech/VokaWebOSTech.ts b/src/internal/player/native/webos/tech/VokaWebOSTech.ts index c5e57ec..a4e282b 100755 --- a/src/internal/player/native/webos/tech/VokaWebOSTech.ts +++ b/src/internal/player/native/webos/tech/VokaWebOSTech.ts @@ -508,6 +508,19 @@ class VokaWebOSTech extends Tech { static get featuresNativeAudioTracks() { return false } + + /** + * Абсолютное время для WebOS (нативное HTML5) не вычисляем — offset=0. + */ + getProgramDateTimeOffset(): number { + return 0 + } + + // Абсолютный диапазон не используем на уровне tech. + + getTimeshiftAvailable(): boolean { + return false + } } VokaWebOSTech.withSourceHandlers(VokaWebOSTech) diff --git a/src/public/VokaPlayerImpl.ts b/src/public/VokaPlayerImpl.ts index 5600452..598ade6 100644 --- a/src/public/VokaPlayerImpl.ts +++ b/src/public/VokaPlayerImpl.ts @@ -722,7 +722,7 @@ export class VokaPlayerImpl implements IVokaPlayer { if (this._player == null) { return null } - return this.getCurrentTime() + return this._player.getAbsoluteCurrentTime() } getAbsoluteTimeRange(): ITimeRange | null {