#68064 Абсолютные значения в hls live
This commit is contained in:
@@ -178,6 +178,7 @@ export const DashErrorTypeIterable = {
|
||||
...DashPlaybackErrorType,
|
||||
...DashDrmErrorType
|
||||
}
|
||||
export type DashProgramDateTimeAPI = Pick<VokaDash, 'getProgramDateTimeOffset' | 'getTimeshiftAvailable'>
|
||||
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() {
|
||||
|
||||
@@ -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<typeof videojs>[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<void> {
|
||||
if (!content.url) {
|
||||
throw new Error('Empty content URL')
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -56,4 +56,4 @@ export default class VokaAppleSourceHandler extends VokaSourceHandler {
|
||||
}
|
||||
|
||||
dispose() {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,4 +40,4 @@ export default class VokaFairplaySourceHandler extends VokaSourceHandler {
|
||||
}
|
||||
|
||||
dispose() {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
export default VokaAppleTech
|
||||
|
||||
@@ -56,4 +56,4 @@ export default class VokaDashSourceHandler extends VokaSourceHandler {
|
||||
}
|
||||
|
||||
dispose() {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
export default VokaDashTech
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -59,4 +59,4 @@ export default class VokaHlsSourceHandler extends VokaSourceHandler {
|
||||
}
|
||||
|
||||
dispose() {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -38,4 +38,4 @@ export default class VokaMp4SourceHandler extends VokaSourceHandler {
|
||||
}
|
||||
|
||||
dispose() {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,4 +225,4 @@ const setupSourceset = function(tech) {
|
||||
}
|
||||
}
|
||||
|
||||
export default setupSourceset
|
||||
export default setupSourceset
|
||||
|
||||
@@ -51,4 +51,4 @@ export default class VokaTizenSourceHandler extends VokaSourceHandler {
|
||||
}
|
||||
|
||||
dispose() {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -115,4 +115,4 @@ export default class VokaWebOSSourceHandler extends VokaSourceHandler {
|
||||
)
|
||||
return nativeDRM
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user