Feature/63538 voka api
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
{
|
||||
"port": 8080,
|
||||
"server": {
|
||||
"baseDir": "./demo"
|
||||
"baseDir": "./demo",
|
||||
"routes": {
|
||||
"/v1/devices.json": "v1/devices.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Title</title>
|
||||
<script language="JavaScript" src="./distribution/vokaPlayer.global.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hello world</h1>
|
||||
<div id="my-video"/>
|
||||
<script language="JavaScript">
|
||||
spbtvplayer('my-video', {
|
||||
features: {
|
||||
api: true,
|
||||
drm: false,
|
||||
metrics: true
|
||||
},
|
||||
apiConfig: {
|
||||
channelId: 'mp4-0aec531fc015',
|
||||
clientId: null,
|
||||
movieId: null,
|
||||
episodeId: null,
|
||||
newsId: null,
|
||||
apiHost: 'localhost:8080',
|
||||
},
|
||||
uiConfig: {
|
||||
initAsLive: true
|
||||
},
|
||||
globalOpts: {
|
||||
uiLanguage: 'ru'
|
||||
},
|
||||
streamOpts: {
|
||||
autoplay: true
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
+17
-3
@@ -9,15 +9,20 @@
|
||||
<h1>Hello world</h1>
|
||||
<div id="my-video"/>
|
||||
<script language="JavaScript">
|
||||
spbtvplayer('my-video', {
|
||||
var player = spbtvplayer('my-video', {
|
||||
features: {
|
||||
api: true,
|
||||
drm: false,
|
||||
metrics: true
|
||||
},
|
||||
apiConfig: {
|
||||
channelId: '998f5396-c9dd-4a1e-82c7-0aec531fc015',
|
||||
urlGetParams: 'minheight=400'
|
||||
channelId: 'hls-998f5396-c9dd-4a1e-82c7-0aec531fc015',
|
||||
urlGetParams: 'minheight=400',
|
||||
clientId: null,
|
||||
movieId: null,
|
||||
episodeId: null,
|
||||
newsId: null,
|
||||
apiHost: 'localhost:8080',
|
||||
},
|
||||
uiConfig: {
|
||||
initAsLive: true
|
||||
@@ -29,6 +34,15 @@
|
||||
autoplay: true
|
||||
}
|
||||
});
|
||||
player.afterInitialize(() => {
|
||||
console.log("Inited 1")
|
||||
})
|
||||
player.afterInitialize(() => {
|
||||
console.log("Inited 2")
|
||||
})
|
||||
player.afterInitialize(() => {
|
||||
console.log("Inited 3")
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,38 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Title</title>
|
||||
<script language="JavaScript" src="./distribution/vokaPlayer.global.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hello world</h1>
|
||||
<div id="my-video"/>
|
||||
<script language="JavaScript">
|
||||
spbtvplayer('my-video', {
|
||||
features: {
|
||||
api: true,
|
||||
drm: false,
|
||||
metrics: true
|
||||
},
|
||||
apiConfig: {
|
||||
channelId: 'mp4-0aec531fc015',
|
||||
clientId: null,
|
||||
movieId: null,
|
||||
episodeId: null,
|
||||
newsId: null,
|
||||
apiHost: 'localhost:8080',
|
||||
},
|
||||
uiConfig: {
|
||||
initAsLive: true
|
||||
},
|
||||
globalOpts: {
|
||||
uiLanguage: 'ru'
|
||||
},
|
||||
streamOpts: {
|
||||
autoplay: true
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"data": {
|
||||
"url": "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8",
|
||||
"subtitles": "https://raw.githubusercontent.com/videojs/video.js/c7298d40a4632a6e9dfcd5a2f5cc3bbe92a78744/docs/examples/elephantsdream/captions.ru.vtt"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"data": {
|
||||
"url": "https://vjs.zencdn.net/v/oceans.mp4",
|
||||
"subtitles": "https://raw.githubusercontent.com/videojs/video.js/c7298d40a4632a6e9dfcd5a2f5cc3bbe92a78744/docs/examples/elephantsdream/captions.ru.vtt"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"data": {
|
||||
"device_token": "SOME_DEVICE_TOKEN"
|
||||
}
|
||||
}
|
||||
@@ -355,7 +355,7 @@ export default class VokaDash {
|
||||
}
|
||||
},
|
||||
debug: {
|
||||
logLevel: options.debug.playerLogs ? Debug.LOG_LEVEL_DEBUG : Debug.LOG_LEVEL_NONE
|
||||
logLevel: (options.debug != undefined && options.debug.playerLogs) ? Debug.LOG_LEVEL_DEBUG : Debug.LOG_LEVEL_NONE
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createEventDefinition } from 'ts-bus'
|
||||
import { Quality } from '@/public/models/ILoadOptions'
|
||||
import { QualityData } from '../player/VokaPlayerCore'
|
||||
import VokaCorePlayer from '../player/VokaCorePlayer'
|
||||
import {
|
||||
VokaInternalErrorComponent,
|
||||
VokaInternalErrorData,
|
||||
@@ -17,7 +17,7 @@ export type ErrorEventData = {
|
||||
|
||||
// Quality
|
||||
export const qualitiesParsed = createEventDefinition<{
|
||||
qualities: QualityData[]
|
||||
qualities: VokaCorePlayer.QualityData[]
|
||||
}>()('quality.parsed')
|
||||
|
||||
export const qualityChange = createEventDefinition<{
|
||||
|
||||
@@ -0,0 +1,385 @@
|
||||
import videojs, { num } from 'video.js'
|
||||
import Player from 'video.js/dist/types/player'
|
||||
import { EventBus } from 'ts-bus'
|
||||
import { VokaPlayerEvent} from '@/public/IVokaPlayer'
|
||||
import { AutoplayChecker } from '@/internal/utils/AutoplayChecker'
|
||||
import { Quality } from '@/public/models/ILoadOptions'
|
||||
import * as languages from '@/languages.json'
|
||||
import '@/plugins/VokaKeyboardPlugin'
|
||||
import '@/plugins/VokaHeartbeatPlugin'
|
||||
import '@/plugins/VokaMetricsPlugin'
|
||||
import '@/plugins/VokaMagicRemotePlugin'
|
||||
|
||||
import '@/components/Skin'
|
||||
import { VokaContentType } from '@/public/models/VokaContentType'
|
||||
import VokaWebOSTech from '@/internal/player/native/webos/tech/VokaWebOSTech'
|
||||
import VokaTizenTech from '@/internal/player/native/tizen/tech/VokaTizenTech'
|
||||
import VokaAppleTech from '@/internal/player/native/apple/tech/VokaAppleTech'
|
||||
import VokaMp4Tech from '@/internal/player/native/mp4/tech/VokaMp4Tech'
|
||||
import VokaHlsTech from '@/internal/player/native/hls/tech/VokaHlsTech'
|
||||
import VokaDashTech from '@/internal/player/native/dash/tech/VokaDashTech'
|
||||
import VokaEmptyTech from '@/internal/player/native/empty/VokaEmptyTech'
|
||||
import { Promise } from 'es6-promise'
|
||||
|
||||
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]
|
||||
const Dom = videojs.dom
|
||||
const DivID = 'vokaPlayerVideoTag'
|
||||
|
||||
export type QualityData = {
|
||||
index: number
|
||||
bitrate: number
|
||||
quality: Quality
|
||||
label?: string
|
||||
}
|
||||
|
||||
export interface IMetrics {
|
||||
url: string
|
||||
interval: number | null
|
||||
params: { [key: string]: string }
|
||||
}
|
||||
|
||||
export enum DRMType {
|
||||
FAIRPLAY = 'fairplay',
|
||||
PLAYREADY = 'playready',
|
||||
WIDEWINE = 'widewine',
|
||||
}
|
||||
|
||||
export interface IDRMConfig {
|
||||
type: DRMType
|
||||
certificateUrl: string // certificate or serverURL
|
||||
licenseUrl: string | null, // licenseUrl
|
||||
headers: [any] | null
|
||||
}
|
||||
|
||||
export interface IContent {
|
||||
url: string,
|
||||
type: VokaContentType,
|
||||
drmConfig: IDRMConfig | null
|
||||
subtitlesUrl: string | null
|
||||
metrics: IMetrics | null
|
||||
}
|
||||
|
||||
export class CorePlayer {
|
||||
|
||||
private player!: Player
|
||||
private autoPlaySupported!: boolean
|
||||
private readonly stateEmitter: EventBus
|
||||
|
||||
// Constructor
|
||||
constructor(element: HTMLElement, callback?: PlayerReadyHandler) {
|
||||
const readyCallback = typeof callback === 'undefined' ? null : callback
|
||||
this.stateEmitter = new EventBus()
|
||||
/*this.stateEmitter.subscribe(
|
||||
playerStateChange,
|
||||
(data) => (this.state_ = data.payload.value)
|
||||
)*/
|
||||
|
||||
AutoplayChecker.isAutoplaySupported((supported) => {
|
||||
this.setupPlayer(element, supported, readyCallback)
|
||||
})
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
public get isPaused(): boolean { return this.player.paused() }
|
||||
|
||||
public get currentTime(): number { return this.player.currentTime() || 0 }
|
||||
|
||||
public get duration(): number { return this.player.duration() || 0 }
|
||||
|
||||
public get volume(): number { return this.player.volume() || 0 }
|
||||
|
||||
public setVolume(value: number) { this.player.volume(value) }
|
||||
|
||||
public get bufferLength(): number { return this.player.bufferedPercent() }
|
||||
|
||||
public get isBuffering(): boolean { return false }
|
||||
|
||||
public seek(seconds: number): void { this.player.currentTime(seconds) }
|
||||
|
||||
public mute(on: boolean) { this.player.muted(on) }
|
||||
|
||||
public play(): Promise<any> | undefined { return this.player.play() }
|
||||
|
||||
public pause() { return this.player.pause() }
|
||||
|
||||
public stop() { return this.player.pause() }
|
||||
|
||||
public get isMuted(): boolean { return this.player.muted() || false }
|
||||
|
||||
public get isLive(): boolean {
|
||||
const duration = this.player.duration()
|
||||
if (duration == undefined) { return true }
|
||||
if (isNaN(duration)) { return false }
|
||||
if (isFinite(duration)) { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
public async load(content: IContent): Promise<void> {
|
||||
|
||||
let playableContent = null
|
||||
const drm = content.drmConfig
|
||||
switch(content.type) {
|
||||
case VokaContentType.HLS:
|
||||
playableContent = {
|
||||
type: "application/x-mpegURL",
|
||||
}
|
||||
break
|
||||
case VokaContentType.MP4:
|
||||
playableContent = {
|
||||
type: "video/mp4",
|
||||
}
|
||||
break
|
||||
case VokaContentType.DASH:
|
||||
playableContent = {
|
||||
type: "application/dash+xml",
|
||||
}
|
||||
break
|
||||
case VokaContentType.WIDEVINE:
|
||||
if (drm != null && drm.type == DRMType.WIDEWINE) {
|
||||
playableContent = {
|
||||
type: "application/dash+xml",
|
||||
keySystemOptions: [{
|
||||
name: "com.widevine.alpha",
|
||||
options: {
|
||||
serverURL: drm.certificateUrl,
|
||||
httpRequestHeaders: drm.headers,
|
||||
priority: 0,
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
break
|
||||
case VokaContentType.PLAYREADY:
|
||||
if (drm != null && drm.type == DRMType.PLAYREADY) {
|
||||
playableContent = {
|
||||
type: "application/dash+xml",
|
||||
keySystemOptions: [{
|
||||
name: "com.microsoft.playready",
|
||||
options: {
|
||||
serverURL: drm.certificateUrl,
|
||||
httpRequestHeaders: drm.headers,
|
||||
priority: 0,
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
break
|
||||
case VokaContentType.FAIRPLAY:
|
||||
if (drm != null && drm.type == DRMType.FAIRPLAY) {
|
||||
playableContent = {
|
||||
type: "application/x-mpegURL",
|
||||
protection: {
|
||||
keySystem: "com.apple.fps.1_0",
|
||||
certificateUrl: drm.certificateUrl,
|
||||
licenseUrl: drm.licenseUrl,
|
||||
},
|
||||
}
|
||||
}
|
||||
break
|
||||
case VokaContentType.AES:
|
||||
playableContent = {
|
||||
type: "application/x-mpegURL",
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if (playableContent != null) {
|
||||
playableContent['src'] = content.url
|
||||
playableContent['content'] = content.type
|
||||
playableContent['metrics'] = content.metrics
|
||||
playableContent['subtitlesUrl'] = content.subtitlesUrl
|
||||
|
||||
this.player.src(playableContent)
|
||||
return Promise.resolve()
|
||||
}
|
||||
return Promise.reject()
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private setupPlayer(
|
||||
element: HTMLElement,
|
||||
autoPlaySupported: boolean,
|
||||
callback: PlayerReadyHandler | null
|
||||
) {
|
||||
this.autoPlaySupported = autoPlaySupported
|
||||
|
||||
// Create main container for player.
|
||||
const playerContainer = Dom.createEl(
|
||||
'video',
|
||||
{ id: `${DivID}_${element.id}_` },
|
||||
{}
|
||||
)
|
||||
element.appendChild(playerContainer)
|
||||
|
||||
videojs.log.level('debug')
|
||||
const options = this.initOptions()
|
||||
const player = videojs(playerContainer, options, () => {
|
||||
if (callback != null) {
|
||||
callback(this)
|
||||
}
|
||||
|
||||
this.player.play()
|
||||
// try load content just right after complete initialization.
|
||||
/*this.stateEmitter.publish(
|
||||
playerStateChange({ value: Initialized })
|
||||
)*/
|
||||
})
|
||||
|
||||
player.addClass('video-js')
|
||||
|
||||
/*if (userAgent.getDevice().type === DEVICE.MOBILE) {
|
||||
player.addClass('vjs-mobile')
|
||||
}*/
|
||||
|
||||
// Sign to events.
|
||||
this.setupAttachListeners(player)
|
||||
this.player = player
|
||||
}
|
||||
|
||||
private initOptions(): VideoJsPlayerOptions {
|
||||
// Required player's plugins.
|
||||
const plugins: Record<string, any> = { }
|
||||
//plugins.vokaQualityPlugin = {}
|
||||
//plugins.vokaStatisticsPlugin = {}
|
||||
//plugins.vokaAdvertisementPlugin = {}
|
||||
//plugins.vokaCaptionsPlugin = {}
|
||||
// plugins.vokaKeyboardPlugin = {
|
||||
// skip: {
|
||||
// forward: 5,
|
||||
// backward: 5,
|
||||
// },
|
||||
// }
|
||||
plugins.vokaMagicRemotePlugin = {
|
||||
skip: {
|
||||
forward: 5,
|
||||
backward: 5,
|
||||
},
|
||||
}
|
||||
plugins.vokaHeartbeatPlugin = {} // Хартбит Player.Heartbeat
|
||||
plugins.vokaMetricsPlugin = {} // Метрики Player.Metrics
|
||||
|
||||
const childrenComponents = [
|
||||
// Non-visual component, not in UI layer list.
|
||||
'mediaLoader' // Required loader, that load tech list!
|
||||
]
|
||||
|
||||
const skinChildren = [
|
||||
'Skin',
|
||||
'resizeManager',
|
||||
'LoadingSpinner',
|
||||
'PosterImage',
|
||||
'RestrictionBox',
|
||||
'liveTracker',
|
||||
]
|
||||
|
||||
childrenComponents.push(...skinChildren)
|
||||
|
||||
// Settings for player.
|
||||
return {
|
||||
techOrder: [
|
||||
VokaWebOSTech.TECH_NAME,
|
||||
VokaTizenTech.TECH_NAME,
|
||||
VokaAppleTech.TECH_NAME,
|
||||
VokaMp4Tech.TECH_NAME,
|
||||
VokaHlsTech.TECH_NAME,
|
||||
VokaDashTech.TECH_NAME,
|
||||
VokaEmptyTech.TECH_NAME
|
||||
], // Order for teches important.
|
||||
plugins, // Attached plugins for player.
|
||||
languages,
|
||||
language: 'ru',
|
||||
children: childrenComponents,
|
||||
poster: undefined,
|
||||
responsive: true,
|
||||
breakpoints: {
|
||||
tiny: undefined,
|
||||
xsmall: undefined,
|
||||
small: 559,
|
||||
medium: 1002,
|
||||
large: undefined,
|
||||
xlarge: undefined,
|
||||
huge: Infinity
|
||||
},
|
||||
playbackRates,
|
||||
tracks: [
|
||||
{
|
||||
kind: "subtitles",
|
||||
src: "https://raw.githubusercontent.com/videojs/video.js/c7298d40a4632a6e9dfcd5a2f5cc3bbe92a78744/docs/examples/elephantsdream/captions.ru.vtt",
|
||||
srclang: 'ru',
|
||||
label: 'russian',
|
||||
},
|
||||
{
|
||||
kind: "subtitles",
|
||||
src: "https://raw.githubusercontent.com/videojs/video.js/c7298d40a4632a6e9dfcd5a2f5cc3bbe92a78744/docs/examples/shared/example-captions.vtt",
|
||||
srclang: 'en',
|
||||
label: 'english',
|
||||
}
|
||||
],
|
||||
qualities: [
|
||||
{
|
||||
res: 480,
|
||||
label: "480p",
|
||||
},
|
||||
{
|
||||
res: 720,
|
||||
label: "720p",
|
||||
},
|
||||
{
|
||||
res: 1080,
|
||||
label: "1080p",
|
||||
}
|
||||
],
|
||||
audio: [
|
||||
{
|
||||
res: "ru",
|
||||
label: "Русский",
|
||||
},
|
||||
{
|
||||
res: "en",
|
||||
label: "Анлийский",
|
||||
},
|
||||
],
|
||||
skip: {
|
||||
forward: 5,
|
||||
backward: 5,
|
||||
},
|
||||
controls: {
|
||||
play: true,
|
||||
replay: true,
|
||||
volume: {
|
||||
inline: true,
|
||||
},
|
||||
progress: true,
|
||||
zoom: true,
|
||||
fullscreen: true,
|
||||
},
|
||||
selection: true,
|
||||
previewPopup: {
|
||||
imageCallback: (percent: number) => {
|
||||
return ''
|
||||
// timeline from voka (for test)
|
||||
// return `https://streaming.voka.tv/vod_preview/velcom/4W17sRxFu8eCAps3kdTxJrFk7d46DNmum_320x180.jpeg?preview_pos=${percent}`;
|
||||
},
|
||||
},
|
||||
enableDocumentPictureInPicture: true,
|
||||
}
|
||||
}
|
||||
|
||||
private setupAttachListeners(player: Player) {
|
||||
// Добавляем листенеры здесь
|
||||
player.on('loadstart', () => {
|
||||
// TODO: удалить, после подключения субтитров не из внешенго источника
|
||||
document.querySelector('.vjs-tech')?.setAttribute('crossOrigin', 'anonymous')
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default VokaCorePlayer
|
||||
@@ -1,294 +0,0 @@
|
||||
import videojs from 'video.js'
|
||||
import Player from 'video.js/dist/types/player'
|
||||
import { EventBus } from 'ts-bus'
|
||||
import { VokaPlayerEvent} from '@/public/IVokaPlayer'
|
||||
import { AutoplayChecker } from '@/internal/utils/AutoplayChecker'
|
||||
import { Quality } from '@/public/models/ILoadOptions'
|
||||
import * as languages from '@/languages.json'
|
||||
import '@/plugins/VokaKeyboardPlugin'
|
||||
import '@/plugins/VokaHeartbeatPlugin'
|
||||
import '@/plugins/VokaMetricsPlugin'
|
||||
import '@/plugins/VokaMagicRemotePlugin'
|
||||
|
||||
import '@/components/Skin'
|
||||
import { VokaContentType } from '@/public/models/VokaContentType'
|
||||
import VokaWebOSTech from '@/internal/player/native/webos/tech/VokaWebOSTech'
|
||||
import VokaTizenTech from '@/internal/player/native/tizen/tech/VokaTizenTech'
|
||||
import VokaAppleTech from '@/internal/player/native/apple/tech/VokaAppleTech'
|
||||
import VokaMp4Tech from '@/internal/player/native/mp4/tech/VokaMp4Tech'
|
||||
import VokaHlsTech from '@/internal/player/native/hls/tech/VokaHlsTech'
|
||||
import VokaDashTech from '@/internal/player/native/dash/tech/VokaDashTech'
|
||||
import VokaEmptyTech from '@/internal/player/native/empty/VokaEmptyTech'
|
||||
|
||||
|
||||
type VideoJsPlayerOptions = Parameters<typeof videojs>[1]
|
||||
type PlayerReadyHandler = (player: VokaPlayerCore) => void
|
||||
|
||||
const playbackRates = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2]
|
||||
const Dom = videojs.dom
|
||||
|
||||
export type QualityData = {
|
||||
index: number
|
||||
bitrate: number
|
||||
quality: Quality
|
||||
label?: string
|
||||
}
|
||||
|
||||
export class VokaPlayerCore {
|
||||
|
||||
static readonly DivID = 'vokaPlayerVideoTag'
|
||||
|
||||
private player!: Player
|
||||
private autoPlaySupported!: boolean
|
||||
private readonly stateEmitter: EventBus
|
||||
|
||||
// Constructor
|
||||
constructor(element: HTMLElement, callback?: PlayerReadyHandler) {
|
||||
const readyCallback = typeof callback === 'undefined' ? null : callback
|
||||
this.stateEmitter = new EventBus()
|
||||
/*this.stateEmitter.subscribe(
|
||||
playerStateChange,
|
||||
(data) => (this.state_ = data.payload.value)
|
||||
)*/
|
||||
|
||||
AutoplayChecker.isAutoplaySupported((supported) => {
|
||||
this.setupPlayer(element, supported, readyCallback)
|
||||
})
|
||||
}
|
||||
|
||||
private setupPlayer(
|
||||
element: HTMLElement,
|
||||
autoPlaySupported: boolean,
|
||||
callback: PlayerReadyHandler | null
|
||||
) {
|
||||
this.autoPlaySupported = autoPlaySupported
|
||||
|
||||
// Create main container for player.
|
||||
const playerContainer = Dom.createEl(
|
||||
'video',
|
||||
{ id: `${VokaPlayerCore.DivID}_${element.id}_` },
|
||||
{}
|
||||
)
|
||||
element.appendChild(playerContainer)
|
||||
|
||||
videojs.log.level('debug')
|
||||
const options = this.initOptions()
|
||||
const player = videojs(playerContainer, options, () => {
|
||||
if (callback != null) {
|
||||
callback(this)
|
||||
}
|
||||
|
||||
this.player.play()
|
||||
// try load content just right after complete initialization.
|
||||
/*this.stateEmitter.publish(
|
||||
playerStateChange({ value: Initialized })
|
||||
)*/
|
||||
})
|
||||
|
||||
player.addClass('video-js')
|
||||
|
||||
/*if (userAgent.getDevice().type === DEVICE.MOBILE) {
|
||||
player.addClass('vjs-mobile')
|
||||
}*/
|
||||
|
||||
// Sign to events.
|
||||
this.setupAttachListeners(player)
|
||||
this.player = player
|
||||
}
|
||||
|
||||
private initOptions(): VideoJsPlayerOptions {
|
||||
// Required player's plugins.
|
||||
const plugins: Record<string, any> = { }
|
||||
//plugins.vokaQualityPlugin = {}
|
||||
//plugins.vokaStatisticsPlugin = {}
|
||||
//plugins.vokaAdvertisementPlugin = {}
|
||||
//plugins.vokaCaptionsPlugin = {}
|
||||
// plugins.vokaKeyboardPlugin = {
|
||||
// skip: {
|
||||
// forward: 5,
|
||||
// backward: 5,
|
||||
// },
|
||||
// }
|
||||
plugins.vokaMagicRemotePlugin = {
|
||||
skip: {
|
||||
forward: 5,
|
||||
backward: 5,
|
||||
},
|
||||
}
|
||||
plugins.vokaHeartbeatPlugin = {} // Хартбит Player.Heartbeat
|
||||
plugins.vokaMetricsPlugin = {} // Метрики Player.Metrics
|
||||
|
||||
const childrenComponents = [
|
||||
// Non-visual component, not in UI layer list.
|
||||
'mediaLoader' // Required loader, that load tech list!
|
||||
]
|
||||
|
||||
const skinChildren = [
|
||||
'Skin',
|
||||
'resizeManager',
|
||||
'LoadingSpinner',
|
||||
'PosterImage',
|
||||
'RestrictionBox',
|
||||
'liveTracker',
|
||||
]
|
||||
|
||||
childrenComponents.push(...skinChildren)
|
||||
|
||||
// Settings for player.
|
||||
return {
|
||||
techOrder: [
|
||||
VokaWebOSTech.TECH_NAME,
|
||||
VokaTizenTech.TECH_NAME,
|
||||
VokaAppleTech.TECH_NAME,
|
||||
VokaMp4Tech.TECH_NAME,
|
||||
VokaHlsTech.TECH_NAME,
|
||||
VokaDashTech.TECH_NAME,
|
||||
VokaEmptyTech.TECH_NAME
|
||||
], // Order for teches important.
|
||||
plugins, // Attached plugins for player.
|
||||
languages,
|
||||
language: 'ru',
|
||||
children: childrenComponents,
|
||||
poster: undefined,
|
||||
responsive: true,
|
||||
breakpoints: {
|
||||
tiny: undefined,
|
||||
xsmall: undefined,
|
||||
small: 559,
|
||||
medium: 1002,
|
||||
large: undefined,
|
||||
xlarge: undefined,
|
||||
huge: Infinity
|
||||
},
|
||||
playbackRates,
|
||||
sources: [
|
||||
// Dash
|
||||
/*{
|
||||
src: "https://dash.akamaized.net/akamai/bbb_30fps/bbb_30fps.mpd",
|
||||
content: VokaContentType.DASH,
|
||||
type: "application/dash+xml"
|
||||
}*/
|
||||
// { src: "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8", type: "application/x-mpegURL" }
|
||||
// { src: "https://dai.google.com/linear/hls/event/rtcMlf4RTvOEkaudeany5w/master.m3u8?iu=/4128/CBS.NY.OTT", type: "application/x-mpegURL" }
|
||||
/*{
|
||||
src: "https://vjs.zencdn.net/v/oceans.mp4",
|
||||
type: "video/mp4",
|
||||
parameters: {
|
||||
stream: {
|
||||
url: "https://vjs.zencdn.net/v/oceans.mp4"
|
||||
}
|
||||
}
|
||||
}*/
|
||||
// Apple Fairplay DRM
|
||||
/*{
|
||||
src: "https://codeeducation.akamaized.net/code/fullcycle/devops_20/01/01_introducao.mp4/fp/fairplay.m3u8",
|
||||
content: VokaContentType.FAIRPLAY,
|
||||
protection: {
|
||||
keySystem: "com.apple.fps.1_0",
|
||||
certificateUrl: "https://codeeducation.akamaized.net/fairplay.cer",
|
||||
licenseUrl: "https://fps.ezdrm.com/api/licenses/F6B15258-BC92-49EB-9CDF-DE9F121C13A5?customdata=MTQ0OmFyZ2VudGluYWx1aXpAZ21haWwuY29tOjY3MTE6Y291cnNlOmNvZGU="
|
||||
}
|
||||
}*/
|
||||
// Dash WideWine DRM
|
||||
/*{
|
||||
src: "https://media.axprod.net/TestVectors/Cmaf/protected_1080p_h264_cbcs/manifest.mpd",
|
||||
content: VokaContentType.WIDEVINE,
|
||||
keySystemOptions: [{
|
||||
name: "com.widevine.alpha",
|
||||
options: {
|
||||
"serverURL": "https://drm-widevine-licensing.axtest.net/AcquireLicense",
|
||||
"httpRequestHeaders": {
|
||||
"X-AxDRM-Message": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.ewogICJ2ZXJzaW9uIjogMSwKICAiY29tX2tleV9pZCI6ICI2OWU1NDA4OC1lOWUwLTQ1MzAtOGMxYS0xZWI2ZGNkMGQxNGUiLAogICJtZXNzYWdlIjogewogICAgInR5cGUiOiAiZW50aXRsZW1lbnRfbWVzc2FnZSIsCiAgICAidmVyc2lvbiI6IDIsCiAgICAibGljZW5zZSI6IHsKICAgICAgImFsbG93X3BlcnNpc3RlbmNlIjogdHJ1ZQogICAgfSwKICAgICJjb250ZW50X2tleXNfc291cmNlIjogewogICAgICAiaW5saW5lIjogWwogICAgICAgIHsKICAgICAgICAgICJpZCI6ICIzMDJmODBkZC00MTFlLTQ4ODYtYmNhNS1iYjFmODAxOGEwMjQiLAogICAgICAgICAgImVuY3J5cHRlZF9rZXkiOiAicm9LQWcwdDdKaTFpNDNmd3YremZ0UT09IiwKICAgICAgICAgICJ1c2FnZV9wb2xpY3kiOiAiUG9saWN5IEEiCiAgICAgICAgfQogICAgICBdCiAgICB9LAogICAgImNvbnRlbnRfa2V5X3VzYWdlX3BvbGljaWVzIjogWwogICAgICB7CiAgICAgICAgIm5hbWUiOiAiUG9saWN5IEEiLAogICAgICAgICJwbGF5cmVhZHkiOiB7CiAgICAgICAgICAibWluX2RldmljZV9zZWN1cml0eV9sZXZlbCI6IDE1MCwKICAgICAgICAgICJwbGF5X2VuYWJsZXJzIjogWwogICAgICAgICAgICAiNzg2NjI3RDgtQzJBNi00NEJFLThGODgtMDhBRTI1NUIwMUE3IgogICAgICAgICAgXQogICAgICAgIH0KICAgICAgfQogICAgXQogIH0KfQ._NfhLVY7S6k8TJDWPeMPhUawhympnrk6WAZHOVjER6M"
|
||||
},
|
||||
priority: 0
|
||||
}
|
||||
}]
|
||||
}*/
|
||||
// Dash Playready DRM
|
||||
/*{
|
||||
src: "https://media.axprod.net/TestVectors/Cmaf/protected_1080p_h264_cbcs/manifest.mpd",
|
||||
content: VokaContentType.PLAYREADY,
|
||||
keySystemOptions: [{
|
||||
name: "com.microsoft.playready",
|
||||
options: {
|
||||
"serverURL": "https://drm-widevine-licensing.axtest.net/AcquireLicense",
|
||||
"httpRequestHeaders": {
|
||||
"X-AxDRM-Message": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.ewogICJ2ZXJzaW9uIjogMSwKICAiY29tX2tleV9pZCI6ICI2OWU1NDA4OC1lOWUwLTQ1MzAtOGMxYS0xZWI2ZGNkMGQxNGUiLAogICJtZXNzYWdlIjogewogICAgInR5cGUiOiAiZW50aXRsZW1lbnRfbWVzc2FnZSIsCiAgICAidmVyc2lvbiI6IDIsCiAgICAibGljZW5zZSI6IHsKICAgICAgImFsbG93X3BlcnNpc3RlbmNlIjogdHJ1ZQogICAgfSwKICAgICJjb250ZW50X2tleXNfc291cmNlIjogewogICAgICAiaW5saW5lIjogWwogICAgICAgIHsKICAgICAgICAgICJpZCI6ICIzMDJmODBkZC00MTFlLTQ4ODYtYmNhNS1iYjFmODAxOGEwMjQiLAogICAgICAgICAgImVuY3J5cHRlZF9rZXkiOiAicm9LQWcwdDdKaTFpNDNmd3YremZ0UT09IiwKICAgICAgICAgICJ1c2FnZV9wb2xpY3kiOiAiUG9saWN5IEEiCiAgICAgICAgfQogICAgICBdCiAgICB9LAogICAgImNvbnRlbnRfa2V5X3VzYWdlX3BvbGljaWVzIjogWwogICAgICB7CiAgICAgICAgIm5hbWUiOiAiUG9saWN5IEEiLAogICAgICAgICJwbGF5cmVhZHkiOiB7CiAgICAgICAgICAibWluX2RldmljZV9zZWN1cml0eV9sZXZlbCI6IDE1MCwKICAgICAgICAgICJwbGF5X2VuYWJsZXJzIjogWwogICAgICAgICAgICAiNzg2NjI3RDgtQzJBNi00NEJFLThGODgtMDhBRTI1NUIwMUE3IgogICAgICAgICAgXQogICAgICAgIH0KICAgICAgfQogICAgXQogIH0KfQ._NfhLVY7S6k8TJDWPeMPhUawhympnrk6WAZHOVjER6M"
|
||||
},
|
||||
priority: 0
|
||||
}
|
||||
}]
|
||||
}*/
|
||||
],
|
||||
tracks: [
|
||||
{
|
||||
kind: "subtitles",
|
||||
src: "https://raw.githubusercontent.com/videojs/video.js/c7298d40a4632a6e9dfcd5a2f5cc3bbe92a78744/docs/examples/elephantsdream/captions.ru.vtt",
|
||||
srclang: 'ru',
|
||||
label: 'russian',
|
||||
},
|
||||
{
|
||||
kind: "subtitles",
|
||||
src: "https://raw.githubusercontent.com/videojs/video.js/c7298d40a4632a6e9dfcd5a2f5cc3bbe92a78744/docs/examples/shared/example-captions.vtt",
|
||||
srclang: 'en',
|
||||
label: 'english',
|
||||
}
|
||||
],
|
||||
qualities: [
|
||||
{
|
||||
res: 480,
|
||||
label: "480p",
|
||||
},
|
||||
{
|
||||
res: 720,
|
||||
label: "720p",
|
||||
},
|
||||
{
|
||||
res: 1080,
|
||||
label: "1080p",
|
||||
}
|
||||
],
|
||||
audio: [
|
||||
{
|
||||
res: "ru",
|
||||
label: "Русский",
|
||||
},
|
||||
{
|
||||
res: "en",
|
||||
label: "Анлийский",
|
||||
},
|
||||
],
|
||||
skip: {
|
||||
forward: 5,
|
||||
backward: 5,
|
||||
},
|
||||
controls: {
|
||||
play: true,
|
||||
replay: true,
|
||||
volume: {
|
||||
inline: true,
|
||||
},
|
||||
progress: true,
|
||||
zoom: true,
|
||||
fullscreen: true,
|
||||
},
|
||||
selection: true,
|
||||
previewPopup: {
|
||||
imageCallback: (percent: number) => {
|
||||
return ''
|
||||
// timeline from voka (for test)
|
||||
// return `https://streaming.voka.tv/vod_preview/velcom/4W17sRxFu8eCAps3kdTxJrFk7d46DNmum_320x180.jpeg?preview_pos=${percent}`;
|
||||
},
|
||||
},
|
||||
enableDocumentPictureInPicture: true,
|
||||
}
|
||||
}
|
||||
|
||||
private setupAttachListeners(player: Player) {
|
||||
// Добавляем листенеры здесь
|
||||
player.on('loadstart', () => {
|
||||
// TODO: удалить, после подключения субтитров не из внешенго источника
|
||||
document.querySelector('.vjs-tech')?.setAttribute('crossOrigin', 'anonymous')
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
VokaOptionsType
|
||||
} from '../../VokaSourceHandler'
|
||||
import { VokaContentType } from '@/public/models/VokaContentType'
|
||||
import { isNativePlayback } from '@/internal/utils/PlatformCapabilities'
|
||||
import PlatformCapabilities from '@/internal/utils/PlatformCapabilities'
|
||||
import VokaAppleTech from '../tech/VokaAppleTech'
|
||||
|
||||
const Tech = videojs.getTech('Tech')
|
||||
@@ -38,7 +38,7 @@ export default class VokaAppleSourceHandler extends VokaSourceHandler {
|
||||
return ''
|
||||
}
|
||||
|
||||
if (isNativePlayback()) {
|
||||
if (PlatformCapabilities.isNativePlayback()) {
|
||||
return 'probably'
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import videojs from "video.js"
|
||||
import VokaEvent from '@/constants/VokaEvent'
|
||||
import VokaDashSourceHandler from '../sourcehandler/VokaDashSourceHandler'
|
||||
import { isSupportedMSE } from '@/internal/utils/PlatformCapabilities'
|
||||
import PlatformCapabilities from '@/internal/utils/PlatformCapabilities'
|
||||
import { DashContext } from '../processors/DashContext'
|
||||
import VokaTech from '@/internal/player/native/VokaTech'
|
||||
import { DashErrorType } from '@/internal/drm/dash/VokaDash'
|
||||
@@ -161,7 +161,7 @@ class VokaDashTech extends VokaTech {
|
||||
}
|
||||
|
||||
return !!(
|
||||
isSupportedMSE() &&
|
||||
PlatformCapabilities.isSupportedMSE() &&
|
||||
VokaDashSourceHandler.VIDEO_TEST_TAG() &&
|
||||
VokaDashSourceHandler.VIDEO_TEST_TAG().canPlayType
|
||||
)
|
||||
|
||||
@@ -7,7 +7,7 @@ import * as Fn from '@/internal/utils/fn'
|
||||
import { wait } from '@/monads/Monoids'
|
||||
import VokaHlsSourceHandler from '@/internal/player/native/hls/sourcehandler/VokaHlsSourceHandler'
|
||||
import HlsLoadContext from '@/internal/player/native/hls/processors/HlsLoadContext'
|
||||
import { isSupportedHlsJs } from '@/internal/utils/PlatformCapabilities'
|
||||
import PlatformCapabilities, { isSupportedHlsJs } from '@/internal/utils/PlatformCapabilities'
|
||||
import VokaEvent from '@/constants/VokaEvent'
|
||||
import { IVokaSource, VokaSourceHandler } from '../../VokaSourceHandler'
|
||||
import { bus } from '@/internal/events/bus'
|
||||
@@ -399,7 +399,7 @@ class VokaHlsTech extends VokaTech {
|
||||
}
|
||||
|
||||
return !!(
|
||||
isSupportedHlsJs &&
|
||||
PlatformCapabilities.isSupportedHlsJs &&
|
||||
VokaHlsSourceHandler.VIDEO_TEST_TAG() &&
|
||||
VokaHlsSourceHandler.VIDEO_TEST_TAG().canPlayType
|
||||
)
|
||||
|
||||
@@ -194,7 +194,7 @@ namespace BrowserUtils {
|
||||
const userAgent = (navigator.userAgent || '').toLowerCase()
|
||||
const vendor = (navigator.vendor || '').toLowerCase()
|
||||
|
||||
[
|
||||
const knownBrowsers =[
|
||||
{ name: 'Edge', prefix: ' edge/' },
|
||||
{ name: 'Edge2', prefix: ' edg/' },
|
||||
{ name: 'Opera', prefix: ' opr/' },
|
||||
@@ -206,7 +206,9 @@ namespace BrowserUtils {
|
||||
{ name: 'Chrome', prefix: ' chrome/', vendor: 'google' },
|
||||
{ name: 'Firefox', prefix: ' firefox/' },
|
||||
{ name: 'IE', prefix: ' trident/' }
|
||||
].forEach((element) => {
|
||||
]
|
||||
|
||||
knownBrowsers.forEach((element) => {
|
||||
if (result.name != null) { return }
|
||||
if (element.vendor != undefined && vendor.indexOf(element.vendor) < 0) { return }
|
||||
|
||||
|
||||
@@ -17,6 +17,19 @@ namespace EncryptSystem {
|
||||
return 'com.apple.fps.1_0'
|
||||
}
|
||||
}
|
||||
|
||||
export function toString(system: EncryptSystem | null): string {
|
||||
switch (system) {
|
||||
case EncryptSystem.playready:
|
||||
return 'playready'
|
||||
case EncryptSystem.widevine:
|
||||
return 'widevine'
|
||||
case EncryptSystem.fairplay:
|
||||
return 'fairplay'
|
||||
default:
|
||||
return 'none'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class EMESystem {
|
||||
|
||||
@@ -2,43 +2,46 @@ import Hls from 'hls.js'
|
||||
import videojs from 'video.js'
|
||||
const Browser = videojs.browser
|
||||
|
||||
/**
|
||||
* Помимо проверки на MSE, выполняет также проверку
|
||||
*
|
||||
* mediaSource.isTypeSupported('video/mp4 codecs="avc1.42E01E,mp4a.40.2"')
|
||||
*/
|
||||
const isSupportedHlsJs: boolean = Hls.isSupported()
|
||||
namespace PlatformCapabilities {
|
||||
|
||||
function getMediaSource(): typeof MediaSource | undefined {
|
||||
return (window as any).MediaSource || (window as any).WebKitMediaSource
|
||||
}
|
||||
/**
|
||||
* Помимо проверки на MSE, выполняет также проверку
|
||||
*
|
||||
* mediaSource.isTypeSupported('video/mp4 codecs="avc1.42E01E,mp4a.40.2"')
|
||||
*/
|
||||
export const isSupportedHlsJs: boolean = Hls.isSupported()
|
||||
|
||||
/**
|
||||
* поддержка MediaSourceExtension - необходимое и, возможно, достаточное (maybe, т.к. до проверки type) условие для работы и hls.js, и DASH.
|
||||
* Код проверки из hls.js isSupported()
|
||||
* но без проверки на isTypeSupported - эту проверку лучше вырполнять не на захардкоденном кодеке, а в canPlayType теча.
|
||||
*/
|
||||
const isSupportedMSE = function (): boolean {
|
||||
const mediaSource = getMediaSource()
|
||||
if (!mediaSource) {
|
||||
return false
|
||||
export function getMediaSource(): typeof MediaSource | undefined {
|
||||
return (window as any).MediaSource || (window as any).WebKitMediaSource
|
||||
}
|
||||
const sourceBuffer: any =
|
||||
self.SourceBuffer || ((self as any).WebKitSourceBuffer as SourceBuffer)
|
||||
|
||||
// if SourceBuffer is exposed ensure its API is valid
|
||||
// safari and old version of Chrome doe not expose SourceBuffer globally so checking SourceBuffer.prototype is impossible
|
||||
const sourceBufferValidAPI =
|
||||
!sourceBuffer ||
|
||||
(sourceBuffer.prototype &&
|
||||
typeof sourceBuffer.prototype.appendBuffer === 'function' &&
|
||||
typeof sourceBuffer.prototype.remove === 'function')
|
||||
return !!sourceBufferValidAPI
|
||||
/**
|
||||
* поддержка MediaSourceExtension - необходимое и, возможно, достаточное (maybe, т.к. до проверки type) условие для работы и hls.js, и DASH.
|
||||
* Код проверки из hls.js isSupported()
|
||||
* но без проверки на isTypeSupported - эту проверку лучше вырполнять не на захардкоденном кодеке, а в canPlayType теча.
|
||||
*/
|
||||
export const isSupportedMSE = function (): boolean {
|
||||
const mediaSource = getMediaSource()
|
||||
if (!mediaSource) {
|
||||
return false
|
||||
}
|
||||
const sourceBuffer: any =
|
||||
self.SourceBuffer || ((self as any).WebKitSourceBuffer as SourceBuffer)
|
||||
|
||||
// if SourceBuffer is exposed ensure its API is valid
|
||||
// safari and old version of Chrome doe not expose SourceBuffer globally so checking SourceBuffer.prototype is impossible
|
||||
const sourceBufferValidAPI =
|
||||
!sourceBuffer ||
|
||||
(sourceBuffer.prototype &&
|
||||
typeof sourceBuffer.prototype.appendBuffer === 'function' &&
|
||||
typeof sourceBuffer.prototype.remove === 'function')
|
||||
return !!sourceBufferValidAPI
|
||||
}
|
||||
|
||||
export const isNativePlayback = function (): boolean {
|
||||
//return !HlsProcessor.isSupported;
|
||||
return Browser.IS_IOS && !isSupportedHlsJs
|
||||
}
|
||||
}
|
||||
|
||||
const isNativePlayback = function (): boolean {
|
||||
//return !HlsProcessor.isSupported;
|
||||
return Browser.IS_IOS && !isSupportedHlsJs
|
||||
}
|
||||
|
||||
export { isSupportedMSE, isSupportedHlsJs, isNativePlayback }
|
||||
export default PlatformCapabilities
|
||||
@@ -27,8 +27,8 @@ namespace SupportedCodecs {
|
||||
}
|
||||
|
||||
export enum NativePlayerType {
|
||||
hls,
|
||||
mss
|
||||
hls = "hls",
|
||||
mss = "mss"
|
||||
}
|
||||
|
||||
export class SupportedCodecs {
|
||||
|
||||
+167
-67
@@ -1,3 +1,5 @@
|
||||
import { VokaOptions } from '@/public/@types'
|
||||
|
||||
export enum VokaPlayerEvent {
|
||||
'DurationChange' = 'durationchange',
|
||||
'TimeUpdate' = 'timeupdate',
|
||||
@@ -57,73 +59,171 @@ toolboxEndSel - toolbox end selection button was clicked
|
||||
toolboxProcessSel - toolbox process selection button was clicked
|
||||
* */
|
||||
|
||||
export interface IVokaPlayer {
|
||||
/// addEventListener - add listener of specific event
|
||||
on(event: VokaPlayerEvent, callback: Function): void
|
||||
/// removeEventListener - remove listener of event
|
||||
off(event: VokaPlayerEvent, callback?: Function): void
|
||||
/// start/resume playing
|
||||
play(): Promise<void>
|
||||
/// pause playing
|
||||
pause(): Promise<void>
|
||||
stop(): Promise<void>
|
||||
export interface ITimeRange {
|
||||
start: number
|
||||
end: number
|
||||
}
|
||||
|
||||
/*
|
||||
load(url: string, options?: ILoadOptions): Promise<void>
|
||||
loadWithProvider(
|
||||
provider: IAPIProvider,
|
||||
options?: ILoadOptions
|
||||
): Promise<void>
|
||||
setCurrentTime(value: number): Promise<void>
|
||||
setVolume(value: number): Promise<void>
|
||||
setPlaybackRate(value: PlaybackRate): Promise<void>
|
||||
setViewType(value: string): Promise<void>
|
||||
setQuality(value: Quality): Promise<void>*/
|
||||
export interface IQuality {
|
||||
bitrate: number | null
|
||||
width: number | null
|
||||
height: number | null
|
||||
}
|
||||
|
||||
/*
|
||||
* afterInitialize - register callback function that will be called after player is initialized (it will be called immediately if player is already initialized)
|
||||
* isInitialized - check if player is initialized
|
||||
* getProtocol - get supported streaming protocol name
|
||||
* getDrmSystem - get supported drm system name
|
||||
* isHlsSupported - check if HLS protocol is supported
|
||||
* getVideoCodecs - get list of supported video codecs for dash protocol (only among those that were enabled in config)
|
||||
* attachSource - set stream url and stream options
|
||||
* getPaused - get paused state
|
||||
* getIsLive - get live streaming flag
|
||||
* seek - seek stream to specific position, in seconds
|
||||
* getCurrentTime - get current stream position, in seconds
|
||||
* getDuration - get current stream duration, in seconds
|
||||
* getAbsoluteCurrentTime - get current utc unixtime for live streams
|
||||
* getAbsoluteTimeRange - get utc start/end range for live streams
|
||||
* getTimeshiftAvailable - check if timeshift is available
|
||||
* getVideoQualityList - get array with information about available video qualities
|
||||
* getSelectedVideoQuality - get index of currently selected video quality (-1 for auto)
|
||||
* getPlayingVideoQuality - get index of currently playing video quality (-1 if unknown)
|
||||
* setSelectedVideoQuality - set index of video quality to play (-1 for auto)
|
||||
* getAudioTrackList - get array of available audio tracks
|
||||
* getCurrentAudioTrack - get index of currently selected audio track
|
||||
* setCurrentAudioTrack - set index of audio track to play
|
||||
* getSubtitlesTrackList - get array of available subtitles tracks
|
||||
* getCurrentSubtitlesTrack - get index of currently selected subtitles track (-1 for disabled)
|
||||
* setCurrentSubtitlesTrack - set index of subtitles track to display (-1 to disable)
|
||||
* setSelectionStartPos - mark current position as selection start
|
||||
* setSelectionEndPos - mark current position as selection end
|
||||
* getSelectionRange - get currently selected range
|
||||
* getZoomButtonVisible - get zoom button visibility
|
||||
* getZoomModeEnabled - check if zoom mode is enabled
|
||||
* setZoomModeEnabled - enable/disable zoom mode (boolean argument)
|
||||
* setVolume - set audio volume
|
||||
* getVolume - get audio volume
|
||||
* mute - mute audio
|
||||
* unmute - unmute audio
|
||||
* getMuted - get muted state
|
||||
* getBufferLength - get length in seconds of buffered data
|
||||
* getNetworkBandwidth - get current network bandwidth in kbit/s
|
||||
* getBufferingState - wheither or not playback is stalled due to buffering
|
||||
* getCurrentVideoInfo - get information about currently playing video track
|
||||
* getControlbarVisible - get visibility state of control bar
|
||||
* getAdIsPlaying - player is currently loading/playing ads
|
||||
* cancelAdPlayback - cancel currently playing advertisement(s)
|
||||
* destroy - destroy "player" object
|
||||
*/
|
||||
export interface IAudioTrack {
|
||||
index: number
|
||||
lang: string
|
||||
label: string
|
||||
}
|
||||
|
||||
export interface ISubtitle {
|
||||
index: number
|
||||
lang: string
|
||||
label: string
|
||||
}
|
||||
|
||||
export interface IVokaPlayer {
|
||||
/// register callback function that will be called after player is initialized (it will be called immediately if player is already initialized)
|
||||
afterInitialize(callback: () => void): void
|
||||
|
||||
/// check if player is initialized
|
||||
isInitialized(): boolean
|
||||
|
||||
/// get supported streaming protocol name
|
||||
getProtocol(): string
|
||||
|
||||
/// get supported drm system name
|
||||
getDrmSystem(): string
|
||||
|
||||
/// check if HLS protocol is supported
|
||||
isHlsSupported(): boolean
|
||||
|
||||
/// get list of supported video codecs for dash protocol (only among those that were enabled in config)
|
||||
getVideoCodecs(): [string]
|
||||
|
||||
/// set stream url and stream options
|
||||
attachSource(url: string, options: VokaOptions.IStreamOptions): void
|
||||
|
||||
/// get paused state
|
||||
getPaused(): boolean
|
||||
|
||||
/// get live streaming flag
|
||||
getIsLive(): boolean
|
||||
|
||||
/// seek stream to specific position, in seconds
|
||||
seek(seconds: number): void
|
||||
|
||||
/// get current stream position, in seconds
|
||||
getCurrentTime(): number
|
||||
|
||||
/// get current stream duration, in seconds
|
||||
getDuration(): number
|
||||
|
||||
/// set audio volume
|
||||
setVolume(value: number)
|
||||
|
||||
/// get audio volume
|
||||
getVolume(): number
|
||||
|
||||
/// mute audio
|
||||
mute(): void
|
||||
|
||||
/// unmute audio
|
||||
unmute(): void
|
||||
|
||||
/// get muted state
|
||||
getMuted(): boolean
|
||||
|
||||
/// destroy
|
||||
destroy()
|
||||
|
||||
/// get length in seconds of buffered data
|
||||
getBufferLength(): number
|
||||
|
||||
/// wheither or not playback is stalled due to buffering
|
||||
getBufferingState(): boolean
|
||||
|
||||
/// player is currently loading/playing ads
|
||||
getAdIsPlaying(): boolean
|
||||
|
||||
/// cancel currently playing advertisement(s)
|
||||
cancelAdPlayback()
|
||||
|
||||
/// get current utc unixtime for live streams
|
||||
getAbsoluteCurrentTime(): number | null
|
||||
|
||||
/// get utc start/end range for live streams
|
||||
getAbsoluteTimeRange(): ITimeRange | null
|
||||
|
||||
/// check if timeshift is available
|
||||
getTimeshiftAvailable(): boolean
|
||||
|
||||
/// get array with information about available video qualities
|
||||
getVideoQualityList(): [IQuality]
|
||||
|
||||
/// get index of currently selected video quality (-1 for auto)
|
||||
getSelectedVideoQuality(): number
|
||||
|
||||
/// get index of currently playing video quality (-1 if unknown)
|
||||
getPlayingVideoQuality(): number
|
||||
|
||||
/// set index of video quality to play (-1 for auto)
|
||||
setSelectedVideoQuality(index: number)
|
||||
|
||||
/// get array of available audio tracks
|
||||
getAudioTrackList(): [IAudioTrack]
|
||||
|
||||
/// get index of currently selected audio track
|
||||
getCurrentAudioTrack(): number
|
||||
|
||||
/// set index of audio track to play
|
||||
setCurrentAudioTrack(index: number)
|
||||
|
||||
/// get array of available subtitles tracks
|
||||
getSubtitlesTrackList(): [ISubtitle]
|
||||
|
||||
/// get index of currently selected subtitles track (-1 for disabled)
|
||||
getCurrentSubtitlesTrack(): number
|
||||
|
||||
/// set index of subtitles track to display (-1 to disable)
|
||||
setCurrentSubtitlesTrack(index: number)
|
||||
|
||||
/// mark current position as selection start
|
||||
setSelectionStartPos(seconds: number)
|
||||
|
||||
/// mark current position as selection end
|
||||
setSelectionEndPos(seconds: number)
|
||||
|
||||
/// get currently selected range
|
||||
getSelectionRange(): ITimeRange | null
|
||||
|
||||
/// get zoom button visibility
|
||||
getZoomButtonVisible(): boolean
|
||||
|
||||
/// check if zoom mode is enabled
|
||||
getZoomModeEnabled(): boolean
|
||||
|
||||
/// enable/disable zoom mode (boolean argument)
|
||||
setZoomModeEnabled(isEnabled: boolean)
|
||||
|
||||
/// get current network bandwidth in kbit/s
|
||||
getNetworkBandwidth(): number
|
||||
|
||||
/// get information about currently playing video track
|
||||
getCurrentVideoInfo(): any
|
||||
|
||||
/// get visibility state of control bar
|
||||
getControlbarVisible(): boolean
|
||||
|
||||
/// addEventListener - add listener of specific event
|
||||
on(event: VokaPlayerEvent, callback: Function): void
|
||||
|
||||
/// removeEventListener - remove listener of event
|
||||
off(event: VokaPlayerEvent, callback?: Function): void
|
||||
|
||||
/// start/resume playing
|
||||
play(): Promise<void>
|
||||
|
||||
/// pause playing
|
||||
pause(): void
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace VokaGlobalFunctions {
|
||||
|
||||
export const value = ""
|
||||
/*
|
||||
GlobalFuncs.log = playerLog;
|
||||
GlobalFuncs.enableExternalLog = enableExternalLog;
|
||||
|
||||
GlobalFuncs.getPlayerVersion = getPlayerVersion;
|
||||
GlobalFuncs.getProtocolAndDrm = getProtocolAndDrm;
|
||||
|
||||
GlobalFuncs.copyOptions = copyOptions;
|
||||
GlobalFuncs.normalizeOptions = normalizeOptions;
|
||||
GlobalFuncs.formatTime = formatTime;
|
||||
* */
|
||||
|
||||
}
|
||||
|
||||
export default VokaGlobalFunctions
|
||||
+21
-29
@@ -1,12 +1,13 @@
|
||||
import { VokaErrorMessages, VokaOptions } from './@types'
|
||||
import { VokaOptions } from './@types'
|
||||
import { IVokaPlayer } from './IVokaPlayer'
|
||||
import { EMESystem, EncryptSystem } from '@/internal/utils/EMESystem'
|
||||
import { VokaPlayerCore } from '@/internal/player/VokaPlayerCore'
|
||||
import VokaCorePlayer from '@/internal/player/VokaCorePlayer'
|
||||
import GUIDUtils from '@/internal/utils/GUIDUtils'
|
||||
import { defaultOptions } from './models/VokaDefaultOptions'
|
||||
import { VokaPlayerImpl } from '@/public/VokaPlayerImpl'
|
||||
import SupportedCodecs from '@/internal/utils/SupportedCodecs'
|
||||
import BrowserUtils from '@/internal/utils/BrowserUtils'
|
||||
import VokaGlobalFunctions from '@/public/VokaGlobalFunctions'
|
||||
|
||||
namespace VokaPlayer {
|
||||
|
||||
@@ -23,10 +24,10 @@ namespace VokaPlayer {
|
||||
|
||||
export function features(): SupportedCodecs.ISelectProtocolResult | null { return this.playerFeatures }
|
||||
|
||||
export async function create(
|
||||
element: HTMLElement | string,
|
||||
export function create(
|
||||
element: HTMLElement | string | null,
|
||||
creationOptions: VokaOptions.IOptions | null
|
||||
): Promise<IVokaPlayer> {
|
||||
): IVokaPlayer | VokaGlobalFunctions {
|
||||
|
||||
let htmlElement: HTMLElement | null = null
|
||||
if (typeof document !== 'undefined') {
|
||||
@@ -38,9 +39,7 @@ namespace VokaPlayer {
|
||||
}
|
||||
|
||||
if (htmlElement == null || !BrowserUtils.isDomElement(htmlElement)) {
|
||||
return Promise.reject(
|
||||
new TypeError(VokaErrorMessages.DocumentUnavailable)
|
||||
)
|
||||
return VokaGlobalFunctions
|
||||
}
|
||||
|
||||
// TODO: Добавить возможность получить предыдущий инстанс плеера передав этот же элемент
|
||||
@@ -52,29 +51,21 @@ namespace VokaPlayer {
|
||||
}*/
|
||||
|
||||
if ((htmlElement as HTMLElement).nodeName == 'IFRAME') {
|
||||
return Promise.reject(
|
||||
new TypeError(VokaErrorMessages.IFrameElement)
|
||||
)
|
||||
return VokaGlobalFunctions
|
||||
}
|
||||
|
||||
const options = { ...defaultOptions, ...creationOptions }
|
||||
|
||||
const result = await detectFeatures(options)
|
||||
this.playerFeatures = Object.freeze(result)
|
||||
|
||||
return new Promise<IVokaPlayer>((resolve) => {
|
||||
new VokaPlayerCore(htmlElement, (player) => {
|
||||
resolve(
|
||||
new VokaPlayerImpl(
|
||||
GUIDUtils.globalUnique, // id
|
||||
GUIDUtils.generateGUID(), // SessionGUID
|
||||
options, // merge options default and passed from outside
|
||||
result,
|
||||
player
|
||||
)
|
||||
)
|
||||
return new VokaPlayerImpl(
|
||||
GUIDUtils.globalUnique, // id
|
||||
GUIDUtils.generateGUID(), // SessionGUID
|
||||
options, // merge options default and passed from outside
|
||||
detectFeatures(options),
|
||||
new Promise<VokaCorePlayer.CorePlayer>((resolve) => {
|
||||
new VokaCorePlayer.CorePlayer(htmlElement, (player) => {
|
||||
resolve(player)
|
||||
})
|
||||
})
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
export async function detectFeatures(options: VokaOptions.IOptions): Promise<SupportedCodecs.ISelectProtocolResult> {
|
||||
@@ -97,8 +88,9 @@ namespace VokaPlayer {
|
||||
try {
|
||||
system = await EMESystem.detect()
|
||||
} catch { }
|
||||
|
||||
return SupportedCodecs.SupportedCodecs.selectProtocol(system, BrowserUtils.isMobile(), options.features.drm, protocolOptions)
|
||||
const result = SupportedCodecs.SupportedCodecs.selectProtocol(system, BrowserUtils.isMobile(), options.features.drm, protocolOptions)
|
||||
this.playerFeatures = Object.freeze(result)
|
||||
return this.playerFeatures
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+250
-20
@@ -1,67 +1,220 @@
|
||||
import { VokaOptions } from './@types'
|
||||
import { IVokaPlayer, VokaPlayerEvent } from './IVokaPlayer'
|
||||
import { VokaPlayerCore } from '@/internal/player/VokaPlayerCore'
|
||||
import { IAudioTrack, IQuality, ISubtitle, ITimeRange, IVokaPlayer, VokaPlayerEvent } from './IVokaPlayer'
|
||||
import VokaCorePlayer from '@/internal/player/VokaCorePlayer'
|
||||
import '../assets/scss/main.scss'
|
||||
import VokaApi from '@/public/network/VokaApi'
|
||||
import BrowserUtils from '@/internal/utils/BrowserUtils'
|
||||
import SupportedCodecs from '@/internal/utils/SupportedCodecs'
|
||||
import { EncryptSystem } from '@/internal/utils/EMESystem'
|
||||
import { VokaContentType } from '@/public/models/VokaContentType'
|
||||
import { Promise } from 'es6-promise'
|
||||
import PlatformCapabilities from '@/internal/utils/PlatformCapabilities'
|
||||
import IResult = VokaApi.IResult
|
||||
import CorePlayer = VokaCorePlayer.CorePlayer
|
||||
|
||||
export class VokaPlayerImpl implements IVokaPlayer {
|
||||
|
||||
private static readonly version = '0.0.3'
|
||||
private static readonly build = '1'
|
||||
|
||||
private readonly player: VokaPlayerCore
|
||||
private _player: VokaCorePlayer.CorePlayer | null
|
||||
public readonly uniqID: number
|
||||
public readonly sessionGUID: string
|
||||
public readonly options: VokaOptions.IOptions
|
||||
|
||||
private readonly initializePromise: Promise<void>
|
||||
private _features: SupportedCodecs.ISelectProtocolResult | null
|
||||
|
||||
public static playerVersion(): string { return `${this.version}.${this.build}` }
|
||||
|
||||
private constructor(
|
||||
id: number,
|
||||
sessionGUID: string,
|
||||
options: VokaOptions.IOptions,
|
||||
features: SupportedCodecs.ISelectProtocolResult,
|
||||
player: VokaPlayerCore
|
||||
features: Promise<SupportedCodecs.ISelectProtocolResult>,
|
||||
playerPromise: Promise<VokaCorePlayer.CorePlayer>
|
||||
) {
|
||||
this.uniqID = id
|
||||
this.sessionGUID = sessionGUID
|
||||
this.options = options
|
||||
this.player = player
|
||||
this._player = null
|
||||
|
||||
this.initialize(features)
|
||||
this.initializePromise = new Promise((resolve) => {
|
||||
|
||||
Promise.all([features, playerPromise]).then(
|
||||
([features, player]) => {
|
||||
this._player = player
|
||||
this._features = features
|
||||
this.initialize(player, features).then((result) => { resolve() })
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
// MARK: - IVokaPlayer implementation
|
||||
|
||||
public afterInitialize(callback: () => void) {
|
||||
this.initializePromise.then(() => { callback() })
|
||||
}
|
||||
|
||||
public isInitialized(): boolean {
|
||||
return this._player != null
|
||||
}
|
||||
|
||||
public getProtocol(): string {
|
||||
const features = this._features
|
||||
if (features != null && features.native != null) {
|
||||
return features.native.toString()
|
||||
}
|
||||
return 'dash'
|
||||
}
|
||||
|
||||
public getDrmSystem(): string {
|
||||
const features = this._features
|
||||
if (!this.options.features.drm || features == null) {
|
||||
return 'none'
|
||||
}
|
||||
return EncryptSystem.toString(features.keySystem)
|
||||
}
|
||||
|
||||
public isHlsSupported(): boolean {
|
||||
return PlatformCapabilities.isSupportedHlsJs
|
||||
}
|
||||
|
||||
public getVideoCodecs(): [string] {
|
||||
const features = this._features
|
||||
if (features == null) { return [] }
|
||||
return features.mseCodecs
|
||||
}
|
||||
|
||||
public attachSource(url: string, options: VokaOptions.IStreamOptions): void {
|
||||
// TODO!!!!
|
||||
}
|
||||
|
||||
public getPaused(): boolean {
|
||||
if (this._player == null) { return false }
|
||||
return this._player.isPaused
|
||||
}
|
||||
|
||||
public getIsLive(): boolean {
|
||||
if (this._player == null) { return false }
|
||||
return this._player.isLive
|
||||
}
|
||||
|
||||
public seek(seconds: number): void {
|
||||
if (this._player == null) { return }
|
||||
this._player.seek(seconds)
|
||||
}
|
||||
|
||||
public getCurrentTime(): number {
|
||||
if (this._player == null) { return 0 }
|
||||
return this._player.currentTime
|
||||
}
|
||||
|
||||
public getDuration(): number {
|
||||
if (this._player == null) { return 0 }
|
||||
return this._player.duration
|
||||
}
|
||||
|
||||
public setVolume(value: number) {
|
||||
if (this._player == null) { return }
|
||||
this._player.setVolume(value)
|
||||
}
|
||||
|
||||
public getVolume(): number {
|
||||
if (this._player == null) { return 0 }
|
||||
return this._player.volume
|
||||
}
|
||||
|
||||
public mute() {
|
||||
if (this._player == null) { return }
|
||||
this._player.mute(true)
|
||||
}
|
||||
|
||||
public unmute() {
|
||||
if (this._player == null) { return }
|
||||
this._player.mute(false)
|
||||
}
|
||||
|
||||
public getMuted(): boolean {
|
||||
if (this._player == null) { return false }
|
||||
return this._player.isMuted
|
||||
}
|
||||
|
||||
public destroy() {
|
||||
// TODO!!!
|
||||
}
|
||||
|
||||
getBufferLength(): number {
|
||||
if (this._player == null) { return 0 }
|
||||
return this._player.bufferLength
|
||||
}
|
||||
getBufferingState(): boolean {
|
||||
if (this._player == null) { return false }
|
||||
return this._player.isBuffering
|
||||
}
|
||||
getAdIsPlaying(): boolean { return false }
|
||||
cancelAdPlayback() {
|
||||
// TODO
|
||||
}
|
||||
getAbsoluteCurrentTime(): number | null { return null }
|
||||
getAbsoluteTimeRange(): ITimeRange | null { return null }
|
||||
getTimeshiftAvailable(): boolean { return false }
|
||||
getVideoQualityList(): [IQuality] { return [] }
|
||||
getSelectedVideoQuality(): number { return 0 }
|
||||
getPlayingVideoQuality(): number { return 0 }
|
||||
setSelectedVideoQuality(index: number) { }
|
||||
getAudioTrackList(): [IAudioTrack] { return [] }
|
||||
getCurrentAudioTrack(): number { return 0 }
|
||||
setCurrentAudioTrack(index: number) {}
|
||||
getSubtitlesTrackList(): [ISubtitle] { return [] }
|
||||
getCurrentSubtitlesTrack(): number { return 0 }
|
||||
setCurrentSubtitlesTrack(index: number) {}
|
||||
setSelectionStartPos(seconds: number) {}
|
||||
setSelectionEndPos(seconds: number) {}
|
||||
getSelectionRange(): ITimeRange | null { return null }
|
||||
getZoomButtonVisible(): boolean { return false }
|
||||
getZoomModeEnabled(): boolean { return false }
|
||||
setZoomModeEnabled(isEnabled: boolean) {}
|
||||
getNetworkBandwidth(): number { return 0 }
|
||||
getCurrentVideoInfo(): any { return {} }
|
||||
getControlbarVisible(): boolean { return true }
|
||||
|
||||
on(event: VokaPlayerEvent, callback: Function): void {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
off(event: VokaPlayerEvent, callback?: Function): void {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
play(): Promise<void> {
|
||||
throw new Error('Method not implemented.')
|
||||
play(): Promise<void> | undefined {
|
||||
if (this._player == null) { return Promise.resolve() }
|
||||
return this._player.play()
|
||||
}
|
||||
pause(): Promise<void> {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
stop(): Promise<void> {
|
||||
throw new Error('Method not implemented.')
|
||||
pause(): void {
|
||||
if (this._player == null) { return }
|
||||
return this._player.pause()
|
||||
}
|
||||
|
||||
// MARK: - Private methods
|
||||
|
||||
private async initialize(features: SupportedCodecs.ISelectProtocolResult) {
|
||||
private load(content: IResult, player: CorePlayer) {
|
||||
|
||||
if (this.options.features.api) {
|
||||
const content = this.fetchContent(this.options, features)
|
||||
}
|
||||
const iContent = {
|
||||
url: content.url,
|
||||
type: VokaContentType.HLS,
|
||||
subtitlesUrl: content.subtitlesUrl,
|
||||
} as VokaCorePlayer.IContent
|
||||
|
||||
player.load(iContent)
|
||||
}
|
||||
|
||||
private async initialize(player: CorePlayer, features: SupportedCodecs.ISelectProtocolResult): Promise<boolean> {
|
||||
if (!this.options.features.api) { return false }
|
||||
|
||||
const content = await this.fetchContent(this.options, features)
|
||||
this.load(content, player)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private async fetchContent(options: VokaOptions.IOptions, features: SupportedCodecs.ISelectProtocolResult) {
|
||||
@@ -97,6 +250,83 @@ export class VokaPlayerImpl implements IVokaPlayer {
|
||||
} as VokaApi.ISystemCapability
|
||||
|
||||
const api = new VokaApi.Api(apiOptions, apiCapability)
|
||||
const result = api.load()
|
||||
return api.load()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MP4
|
||||
/*const iContent = {
|
||||
url: "https://vjs.zencdn.net/v/oceans.mp4",
|
||||
type: VokaContentType.MP4,
|
||||
} as VokaCorePlayer.IContent*/
|
||||
|
||||
// HLS
|
||||
/*const iContent = {
|
||||
url: "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8",
|
||||
type: VokaContentType.HLS,
|
||||
} as VokaCorePlayer.IContent*/
|
||||
|
||||
// HLS LIVE
|
||||
/*const iContent = {
|
||||
url: "https://dai.google.com/linear/hls/event/rtcMlf4RTvOEkaudeany5w/master.m3u8?iu=/4128/CBS.NY.OTT",
|
||||
type: VokaContentType.HLS,
|
||||
} as VokaCorePlayer.IContent*/
|
||||
|
||||
// DASH
|
||||
/*const iContent = {
|
||||
url: "https://dash.akamaized.net/akamai/bbb_30fps/bbb_30fps.mpd",
|
||||
type: VokaContentType.DASH,
|
||||
} as VokaCorePlayer.IContent*/
|
||||
|
||||
// DASH
|
||||
/*const iContent = {
|
||||
url: "https://dash.akamaized.net/akamai/bbb_30fps/bbb_30fps.mpd",
|
||||
type: VokaContentType.DASH,
|
||||
} as VokaCorePlayer.IContent*/
|
||||
|
||||
// FAIRPLAY TODO
|
||||
/*const iContent = {
|
||||
url: "https://codeeducation.akamaized.net/code/fullcycle/devops_20/01/01_introducao.mp4/fp/fairplay.m3u8",
|
||||
type: VokaContentType.FAIRPLAY,
|
||||
drmConfig: {
|
||||
type: VokaCorePlayer.DRMType.FAIRPLAY,
|
||||
certificateUrl: "https://codeeducation.akamaized.net/fairplay.cer",
|
||||
licenseUrl: "https://fps.ezdrm.com/api/licenses/F6B15258-BC92-49EB-9CDF-DE9F121C13A5?customdata=MTQ0OmFyZ2VudGluYWx1aXpAZ21haWwuY29tOjY3MTE6Y291cnNlOmNvZGU=",
|
||||
}
|
||||
} as VokaCorePlayer.IContent*/
|
||||
|
||||
/*const iContent = {
|
||||
url: "https://e09f957480c8b1e479a1edb0fabc72d8.egress.mediapackage-vod.eu-west-1.amazonaws.com/out/v1/6f12444e79macdf3e4206ad363f810cb2aead/9ea4e8148b794c8ba2c6295b824e5ad5/46a61bf2c081464bb9476f2a55a06f48/index.m3u8",
|
||||
type: VokaContentType.FAIRPLAY,
|
||||
drmConfig: {
|
||||
type: VokaCorePlayer.DRMType.FAIRPLAY,
|
||||
certificateUrl: "https://customer-tests.la.drm.cloud/certificate/fairplay?BrandGuid=5a96a0d0-d13f-42b0-ab2b-ba8cfc4aa0a0",
|
||||
licenseUrl: "https://customer-tests.la.drm.cloud/acquire-license/fairplay?KID=4376a4b3-d8ef-4f21-9a6b-faa81a2e59e3&brandguid=5a96a0d0-d13f-42b0-ab2b-ba8cfc4aa0a0&usertoken=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3MzU2ODk2MDAsImRybVRva2VuSW5mbyI6eyJleHAiOiIyMDI1LTEyLTA3VDE1OjMwOjA5LjU5MDgxMjUrMDE6MDAiLCJraWQiOlsiKiJdLCJwIjp7InBlcnMiOnRydWUsImVkIjoiMjAyNS0xMi0wN1QxNTozMDowOS41OTExMzA1KzAxOjAwIn19fQ.xEToUttAk9AVFgP3bHyDlcvm6BR-8_hsl8V3n-jrDwM",
|
||||
}
|
||||
} as VokaCorePlayer.IContent*/
|
||||
|
||||
// Dash WideWine DRM
|
||||
/*const iContent = {
|
||||
url: "https://media.axprod.net/TestVectors/Cmaf/protected_1080p_h264_cbcs/manifest.mpd",
|
||||
type: VokaContentType.WIDEVINE,
|
||||
drmConfig: {
|
||||
type: VokaCorePlayer.DRMType.WIDEWINE,
|
||||
certificateUrl: "https://drm-widevine-licensing.axtest.net/AcquireLicense",
|
||||
headers: {
|
||||
"X-AxDRM-Message": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.ewogICJ2ZXJzaW9uIjogMSwKICAiY29tX2tleV9pZCI6ICI2OWU1NDA4OC1lOWUwLTQ1MzAtOGMxYS0xZWI2ZGNkMGQxNGUiLAogICJtZXNzYWdlIjogewogICAgInR5cGUiOiAiZW50aXRsZW1lbnRfbWVzc2FnZSIsCiAgICAidmVyc2lvbiI6IDIsCiAgICAibGljZW5zZSI6IHsKICAgICAgImFsbG93X3BlcnNpc3RlbmNlIjogdHJ1ZQogICAgfSwKICAgICJjb250ZW50X2tleXNfc291cmNlIjogewogICAgICAiaW5saW5lIjogWwogICAgICAgIHsKICAgICAgICAgICJpZCI6ICIzMDJmODBkZC00MTFlLTQ4ODYtYmNhNS1iYjFmODAxOGEwMjQiLAogICAgICAgICAgImVuY3J5cHRlZF9rZXkiOiAicm9LQWcwdDdKaTFpNDNmd3YremZ0UT09IiwKICAgICAgICAgICJ1c2FnZV9wb2xpY3kiOiAiUG9saWN5IEEiCiAgICAgICAgfQogICAgICBdCiAgICB9LAogICAgImNvbnRlbnRfa2V5X3VzYWdlX3BvbGljaWVzIjogWwogICAgICB7CiAgICAgICAgIm5hbWUiOiAiUG9saWN5IEEiLAogICAgICAgICJwbGF5cmVhZHkiOiB7CiAgICAgICAgICAibWluX2RldmljZV9zZWN1cml0eV9sZXZlbCI6IDE1MCwKICAgICAgICAgICJwbGF5X2VuYWJsZXJzIjogWwogICAgICAgICAgICAiNzg2NjI3RDgtQzJBNi00NEJFLThGODgtMDhBRTI1NUIwMUE3IgogICAgICAgICAgXQogICAgICAgIH0KICAgICAgfQogICAgXQogIH0KfQ._NfhLVY7S6k8TJDWPeMPhUawhympnrk6WAZHOVjER6M",
|
||||
},
|
||||
}
|
||||
} as VokaCorePlayer.IContent*/
|
||||
|
||||
// Dash Playready DRM
|
||||
/*const iContent = {
|
||||
url: "https://media.axprod.net/TestVectors/Cmaf/protected_1080p_h264_cbcs/manifest.mpd",
|
||||
type: VokaContentType.PLAYREADY,
|
||||
drmConfig: {
|
||||
type: VokaCorePlayer.DRMType.PLAYREADY,
|
||||
certificateUrl: "https://drm-widevine-licensing.axtest.net/AcquireLicense",
|
||||
headers: {
|
||||
"X-AxDRM-Message": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.ewogICJ2ZXJzaW9uIjogMSwKICAiY29tX2tleV9pZCI6ICI2OWU1NDA4OC1lOWUwLTQ1MzAtOGMxYS0xZWI2ZGNkMGQxNGUiLAogICJtZXNzYWdlIjogewogICAgInR5cGUiOiAiZW50aXRsZW1lbnRfbWVzc2FnZSIsCiAgICAidmVyc2lvbiI6IDIsCiAgICAibGljZW5zZSI6IHsKICAgICAgImFsbG93X3BlcnNpc3RlbmNlIjogdHJ1ZQogICAgfSwKICAgICJjb250ZW50X2tleXNfc291cmNlIjogewogICAgICAiaW5saW5lIjogWwogICAgICAgIHsKICAgICAgICAgICJpZCI6ICIzMDJmODBkZC00MTFlLTQ4ODYtYmNhNS1iYjFmODAxOGEwMjQiLAogICAgICAgICAgImVuY3J5cHRlZF9rZXkiOiAicm9LQWcwdDdKaTFpNDNmd3YremZ0UT09IiwKICAgICAgICAgICJ1c2FnZV9wb2xpY3kiOiAiUG9saWN5IEEiCiAgICAgICAgfQogICAgICBdCiAgICB9LAogICAgImNvbnRlbnRfa2V5X3VzYWdlX3BvbGljaWVzIjogWwogICAgICB7CiAgICAgICAgIm5hbWUiOiAiUG9saWN5IEEiLAogICAgICAgICJwbGF5cmVhZHkiOiB7CiAgICAgICAgICAibWluX2RldmljZV9zZWN1cml0eV9sZXZlbCI6IDE1MCwKICAgICAgICAgICJwbGF5X2VuYWJsZXJzIjogWwogICAgICAgICAgICAiNzg2NjI3RDgtQzJBNi00NEJFLThGODgtMDhBRTI1NUIwMUE3IgogICAgICAgICAgXQogICAgICAgIH0KICAgICAgfQogICAgXQogIH0KfQ._NfhLVY7S6k8TJDWPeMPhUawhympnrk6WAZHOVjER6M",
|
||||
},
|
||||
}
|
||||
} as VokaCorePlayer.IContent*/
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createEventDefinition, EventBus } from 'ts-bus'
|
||||
import { HTTPClient, HTTPMethod, IHTTPClient, RequestOptions } from '../../internal/utils/HTTPClient'
|
||||
import { VokaOptions } from '@/public/@types'
|
||||
|
||||
namespace VokaApi {
|
||||
|
||||
@@ -67,7 +68,8 @@ namespace VokaApi {
|
||||
url: string
|
||||
subtitlesUrl: string | null
|
||||
drmConfig: IDRMConfig | null
|
||||
metrics: IMetrics | null
|
||||
metrics: IMetrics | null,
|
||||
streamOptions: VokaOptions.IStreamOptions | null
|
||||
}
|
||||
|
||||
export class Api {
|
||||
@@ -135,7 +137,7 @@ namespace VokaApi {
|
||||
}
|
||||
|
||||
const result = await client.request(
|
||||
HTTPMethod.POST,
|
||||
HTTPMethod.GET,// HTTPMethod.POST,
|
||||
"//" + this.getApiHost() + "/v1/devices.json",
|
||||
queryParams,
|
||||
null,
|
||||
|
||||
Reference in New Issue
Block a user