@videojs/http-streaming
Advanced tools
Comparing version 3.12.2 to 3.13.0
{ | ||
"name": "@videojs/http-streaming", | ||
"version": "3.12.2", | ||
"version": "3.13.0", | ||
"description": "Play back HLS and DASH with Video.js, even where it's not natively supported", | ||
@@ -70,3 +70,3 @@ "main": "dist/videojs-http-streaming.cjs.js", | ||
"peerDependencies": { | ||
"video.js": "^8.11.0" | ||
"video.js": "^8.14.0" | ||
}, | ||
@@ -92,3 +92,3 @@ "devDependencies": { | ||
"url-toolkit": "^2.2.1", | ||
"videojs-contrib-eme": "^5.0.1", | ||
"videojs-contrib-eme": "^5.3.1", | ||
"videojs-contrib-quality-levels": "^4.0.0", | ||
@@ -98,3 +98,3 @@ "videojs-generate-karma-config": "^8.0.1", | ||
"videojs-generator-verify": "~3.0.1", | ||
"videojs-standard": "^9.0.0", | ||
"videojs-standard": "^9.1.0", | ||
"water-plant-uml": "^2.0.2" | ||
@@ -101,0 +101,0 @@ }, |
@@ -172,3 +172,9 @@ import resolveUrl from './resolve-url'; | ||
} | ||
const metadata = { | ||
contentSteeringInfo: { | ||
uri | ||
} | ||
}; | ||
this.trigger({ type: 'contentsteeringloadstart', metadata }); | ||
this.request_ = this.xhr_({ | ||
@@ -209,5 +215,27 @@ uri, | ||
} | ||
const steeringManifestJson = JSON.parse(this.request_.responseText); | ||
this.trigger({ type: 'contentsteeringloadcomplete', metadata }); | ||
let steeringManifestJson; | ||
try { | ||
steeringManifestJson = JSON.parse(this.request_.responseText); | ||
} catch (parseError) { | ||
const errorMetadata = { | ||
errorType: videojs.Error.StreamingContentSteeringParserError, | ||
error: parseError | ||
}; | ||
this.trigger({ type: 'error', metadata: errorMetadata }); | ||
} | ||
this.assignSteeringProperties_(steeringManifestJson); | ||
const parsedMetadata = { | ||
contentSteeringInfo: metadata.contentSteeringInfo, | ||
contentSteeringManifest: { | ||
version: this.steeringManifest.version, | ||
reloadUri: this.steeringManifest.reloadUri, | ||
priority: this.steeringManifest.priority | ||
} | ||
}; | ||
this.trigger({ type: 'contentsteeringparsed', metadata: parsedMetadata }); | ||
this.startTTLTimeout_(); | ||
@@ -214,0 +242,0 @@ }); |
@@ -25,2 +25,3 @@ import videojs from 'video.js'; | ||
import {merge} from './util/vjs-compat'; | ||
import { getStreamingNetworkErrorMetadata } from './error-codes.js'; | ||
@@ -395,3 +396,2 @@ const { EventTarget } = videojs; | ||
const fin = (err, request) => { | ||
// TODO: add error metdata here once we create an error type in video.js | ||
if (this.requestErrored_(err, request, startingState)) { | ||
@@ -402,2 +402,3 @@ return; | ||
const sidxMapping = this.mainPlaylistLoader_.sidxMapping_; | ||
const { requestType } = request; | ||
let sidx; | ||
@@ -408,5 +409,3 @@ | ||
} catch (e) { | ||
e.metadata = { | ||
errorType: videojs.Error.DashManifestSidxParsingError | ||
}; | ||
e.metadata = getStreamingNetworkErrorMetadata({requestType, request, parseFailure: true }); | ||
@@ -427,2 +426,3 @@ // sidx parsing failed. | ||
}; | ||
const REQUEST_TYPE = 'dash-sidx'; | ||
@@ -447,7 +447,3 @@ this.request = containerRequest(uri, this.vhs_.xhr, (err, request, container, bytes) => { | ||
// MEDIA_ERR_NETWORK | ||
code: 2, | ||
metadata: { | ||
errorType: videojs.Error.UnsupportedSidxContainer, | ||
sidxContainer | ||
} | ||
code: 2 | ||
}, request); | ||
@@ -471,5 +467,6 @@ } | ||
responseType: 'arraybuffer', | ||
requestType: 'dash-sidx', | ||
headers: segmentXhrHeaders({byterange: playlist.sidx.byterange}) | ||
}, fin); | ||
}); | ||
}, REQUEST_TYPE); | ||
} | ||
@@ -656,2 +653,9 @@ | ||
requestMain_(cb) { | ||
const metadata = { | ||
manifestInfo: { | ||
uri: this.mainPlaylistLoader_.srcUrl | ||
} | ||
}; | ||
this.trigger({type: 'manifestrequeststart', metadata}); | ||
this.request = this.vhs_.xhr({ | ||
@@ -662,2 +666,8 @@ uri: this.mainPlaylistLoader_.srcUrl, | ||
}, (error, req) => { | ||
if (error) { | ||
const { requestType } = req; | ||
error.metadata = getStreamingNetworkErrorMetadata({ requestType, request: req, error }); | ||
} | ||
if (this.requestErrored_(error, req)) { | ||
@@ -669,2 +679,3 @@ if (this.state === 'HAVE_NOTHING') { | ||
} | ||
this.trigger({type: 'manifestrequestcomplete', metadata}); | ||
@@ -730,2 +741,5 @@ const mainChanged = req.responseText !== this.mainPlaylistLoader_.mainXml_; | ||
if (error) { | ||
const { requestType } = req; | ||
this.error.metadata = getStreamingNetworkErrorMetadata({ requestType, request: req, error }); | ||
// sync request failed, fall back to using date header from mpd | ||
@@ -776,11 +790,28 @@ // TODO: log warning | ||
const oldMain = this.mainPlaylistLoader_.main; | ||
const metadata = { | ||
manifestInfo: { | ||
uri: this.mainPlaylistLoader_.srcUrl | ||
} | ||
}; | ||
let newMain = parseMainXml({ | ||
mainXml: this.mainPlaylistLoader_.mainXml_, | ||
srcUrl: this.mainPlaylistLoader_.srcUrl, | ||
clientOffset: this.mainPlaylistLoader_.clientOffset_, | ||
sidxMapping: this.mainPlaylistLoader_.sidxMapping_, | ||
previousManifest: oldMain | ||
}); | ||
this.trigger({type: 'manifestparsestart', metadata}); | ||
let newMain; | ||
try { | ||
newMain = parseMainXml({ | ||
mainXml: this.mainPlaylistLoader_.mainXml_, | ||
srcUrl: this.mainPlaylistLoader_.srcUrl, | ||
clientOffset: this.mainPlaylistLoader_.clientOffset_, | ||
sidxMapping: this.mainPlaylistLoader_.sidxMapping_, | ||
previousManifest: oldMain | ||
}); | ||
} catch (error) { | ||
this.error = error; | ||
this.error.metadata = { | ||
errorType: videojs.Error.StreamingDashManifestParserError, | ||
error | ||
}; | ||
this.trigger('error'); | ||
} | ||
// if we have an old main to compare the new main against | ||
@@ -804,3 +835,24 @@ if (oldMain) { | ||
this.addEventStreamToMetadataTrack_(newMain); | ||
if (newMain) { | ||
const { duration, endList } = newMain; | ||
const renditions = []; | ||
newMain.playlists.forEach((playlist) => { | ||
renditions.push({ | ||
id: playlist.id, | ||
bandwidth: playlist.attributes.BANDWIDTH, | ||
resolution: playlist.attributes.RESOLUTION, | ||
codecs: playlist.attributes.CODECS | ||
}); | ||
}); | ||
const parsedManifest = { | ||
duration, | ||
isLive: !endList, | ||
renditions | ||
}; | ||
metadata.parsedManifest = parsedManifest; | ||
this.trigger({type: 'manifestparsecomplete', metadata}); | ||
} | ||
return Boolean(newMain); | ||
@@ -807,0 +859,0 @@ } |
@@ -0,2 +1,32 @@ | ||
import videojs from 'video.js'; | ||
// https://www.w3.org/TR/WebIDL-1/#quotaexceedederror | ||
export const QUOTA_EXCEEDED_ERR = 22; | ||
export const getStreamingNetworkErrorMetadata = ({ requestType, request, error, parseFailure }) => { | ||
const isBadStatus = request.status < 200 || request.status > 299; | ||
const isFailure = request.status >= 400 && request.status <= 499; | ||
const errorMetadata = { | ||
uri: request.uri, | ||
requestType | ||
}; | ||
const isBadStatusOrParseFailure = (isBadStatus && !isFailure) || parseFailure; | ||
if (error && isFailure) { | ||
// copy original error and add to the metadata. | ||
errorMetadata.error = {...error}; | ||
errorMetadata.errorType = videojs.Error.NetworkRequestFailed; | ||
} else if (request.aborted) { | ||
errorMetadata.errorType = videojs.Error.NetworkRequestAborted; | ||
} else if (request.timedout) { | ||
errorMetadata.erroType = videojs.Error.NetworkRequestTimeout; | ||
} else if (isBadStatusOrParseFailure) { | ||
const errorType = parseFailure ? videojs.Error.NetworkBodyParserFailed : videojs.Error.NetworkBadStatus; | ||
errorMetadata.errorType = errorType; | ||
errorMetadata.status = request.status; | ||
errorMetadata.headers = request.headers; | ||
} | ||
return errorMetadata; | ||
}; |
@@ -12,2 +12,4 @@ import videojs from 'video.js'; | ||
import {merge} from './util/vjs-compat'; | ||
import { getStreamingNetworkErrorMetadata } from './error-codes.js'; | ||
import { segmentInfoPayload } from './segment-loader.js'; | ||
@@ -76,2 +78,5 @@ export const REQUEST_ERRORS = { | ||
const handleErrors = (error, request) => { | ||
const { requestType } = request; | ||
const metadata = getStreamingNetworkErrorMetadata({ requestType, request, error }); | ||
if (request.timedout) { | ||
@@ -82,3 +87,4 @@ return { | ||
code: REQUEST_ERRORS.TIMEOUT, | ||
xhr: request | ||
xhr: request, | ||
metadata | ||
}; | ||
@@ -92,3 +98,4 @@ } | ||
code: REQUEST_ERRORS.ABORTED, | ||
xhr: request | ||
xhr: request, | ||
metadata | ||
}; | ||
@@ -102,3 +109,4 @@ } | ||
code: REQUEST_ERRORS.FAILURE, | ||
xhr: request | ||
xhr: request, | ||
metadata | ||
}; | ||
@@ -112,3 +120,4 @@ } | ||
code: REQUEST_ERRORS.FAILURE, | ||
xhr: request | ||
xhr: request, | ||
metadata | ||
}; | ||
@@ -130,3 +139,3 @@ } | ||
*/ | ||
const handleKeyResponse = (segment, objects, finishProcessingFn) => (error, request) => { | ||
const handleKeyResponse = (segment, objects, finishProcessingFn, triggerSegmentEventFn) => (error, request) => { | ||
const response = request.response; | ||
@@ -159,3 +168,5 @@ const errorObj = handleErrors(error, request); | ||
} | ||
const keyInfo = { uri: request.uri }; | ||
triggerSegmentEventFn({ type: 'segmentkeyloadcomplete', segment, keyInfo }); | ||
return finishProcessingFn(null, segment); | ||
@@ -178,3 +189,2 @@ }; | ||
metadata: { | ||
errorType: videojs.Error.UnsupportedMediaInitialization, | ||
mediaType | ||
@@ -224,3 +234,3 @@ } | ||
const handleInitSegmentResponse = | ||
({segment, finishProcessingFn}) => (error, request) => { | ||
({segment, finishProcessingFn, triggerSegmentEventFn}) => (error, request) => { | ||
const errorObj = handleErrors(error, request); | ||
@@ -233,2 +243,3 @@ | ||
triggerSegmentEventFn({ type: 'segmentloaded', segment }); | ||
// init segment is encypted, we will have to wait | ||
@@ -268,3 +279,4 @@ // until the key request is done to decrypt. | ||
finishProcessingFn, | ||
responseType | ||
responseType, | ||
triggerSegmentEventFn | ||
}) => (error, request) => { | ||
@@ -276,3 +288,3 @@ const errorObj = handleErrors(error, request); | ||
} | ||
triggerSegmentEventFn({ type: 'segmentloaded', segment }); | ||
const newBytes = | ||
@@ -312,3 +324,4 @@ // although responseText "should" exist, this guard serves to prevent an error being | ||
doneFn, | ||
onTransmuxerLog | ||
onTransmuxerLog, | ||
triggerSegmentEventFn | ||
}) => { | ||
@@ -367,5 +380,29 @@ const fmp4Tracks = segment.map && segment.map.tracks || {}; | ||
onVideoSegmentTimingInfo: (videoSegmentTimingInfo) => { | ||
const timingInfo = { | ||
pts: { | ||
start: videoSegmentTimingInfo.start.presentation, | ||
end: videoSegmentTimingInfo.end.presentation | ||
}, | ||
dts: { | ||
start: videoSegmentTimingInfo.start.decode, | ||
end: videoSegmentTimingInfo.end.decode | ||
} | ||
}; | ||
triggerSegmentEventFn({ type: 'segmenttransmuxingtiminginfoavailable', segment, timingInfo }); | ||
videoSegmentTimingInfoFn(videoSegmentTimingInfo); | ||
}, | ||
onAudioSegmentTimingInfo: (audioSegmentTimingInfo) => { | ||
const timingInfo = { | ||
pts: { | ||
start: audioSegmentTimingInfo.start.pts, | ||
end: audioSegmentTimingInfo.end.pts | ||
}, | ||
dts: { | ||
start: audioSegmentTimingInfo.start.dts, | ||
end: audioSegmentTimingInfo.end.dts | ||
} | ||
}; | ||
triggerSegmentEventFn({ type: 'segmenttransmuxingtiminginfoavailable', segment, timingInfo }); | ||
audioSegmentTimingInfoFn(audioSegmentTimingInfo); | ||
@@ -384,3 +421,3 @@ }, | ||
onTransmuxerLog, | ||
onDone: (result) => { | ||
onDone: (result, error) => { | ||
if (!doneFn) { | ||
@@ -390,4 +427,7 @@ return; | ||
result.type = result.type === 'combined' ? 'video' : result.type; | ||
doneFn(null, segment, result); | ||
} | ||
triggerSegmentEventFn({ type: 'segmenttransmuxingcomplete', segment }); | ||
doneFn(error, segment, result); | ||
}, | ||
segment, | ||
triggerSegmentEventFn | ||
}); | ||
@@ -435,3 +475,4 @@ | ||
doneFn, | ||
onTransmuxerLog | ||
onTransmuxerLog, | ||
triggerSegmentEventFn | ||
}) => { | ||
@@ -586,7 +627,8 @@ let bytesAsUint8Array = new Uint8Array(bytes); | ||
doneFn, | ||
onTransmuxerLog | ||
onTransmuxerLog, | ||
triggerSegmentEventFn | ||
}); | ||
}; | ||
const decrypt = function({id, key, encryptedBytes, decryptionWorker}, callback) { | ||
const decrypt = function({id, key, encryptedBytes, decryptionWorker, segment, doneFn}, callback) { | ||
const decryptionHandler = (event) => { | ||
@@ -605,4 +647,21 @@ if (event.data.source === id) { | ||
decryptionWorker.onerror = () => { | ||
const message = 'An error occurred in the decryption worker'; | ||
const segmentInfo = segmentInfoPayload({segment}); | ||
const decryptError = { | ||
message, | ||
metadata: { | ||
error: new Error(message), | ||
errorType: videojs.Error.StreamingFailedToDecryptSegment, | ||
segmentInfo, | ||
keyInfo: { | ||
uri: segment.key.resolvedUri || segment.map.key.resolvedUri | ||
} | ||
} | ||
}; | ||
doneFn(decryptError, segment); | ||
}; | ||
decryptionWorker.addEventListener('message', decryptionHandler); | ||
let keyBytes; | ||
@@ -665,4 +724,6 @@ | ||
doneFn, | ||
onTransmuxerLog | ||
onTransmuxerLog, | ||
triggerSegmentEventFn | ||
}) => { | ||
triggerSegmentEventFn({ type: 'segmentdecryptionstart' }); | ||
decrypt({ | ||
@@ -672,5 +733,8 @@ id: segment.requestId, | ||
encryptedBytes: segment.encryptedBytes, | ||
decryptionWorker | ||
decryptionWorker, | ||
segment, | ||
doneFn | ||
}, (decryptedBytes) => { | ||
segment.bytes = decryptedBytes; | ||
triggerSegmentEventFn({ type: 'segmentdecryptioncomplete', segment }); | ||
@@ -690,3 +754,4 @@ handleSegmentBytes({ | ||
doneFn, | ||
onTransmuxerLog | ||
onTransmuxerLog, | ||
triggerSegmentEventFn | ||
}); | ||
@@ -738,3 +803,4 @@ }); | ||
doneFn, | ||
onTransmuxerLog | ||
onTransmuxerLog, | ||
triggerSegmentEventFn | ||
}) => { | ||
@@ -786,3 +852,4 @@ let count = 0; | ||
doneFn, | ||
onTransmuxerLog | ||
onTransmuxerLog, | ||
triggerSegmentEventFn | ||
}); | ||
@@ -804,3 +871,4 @@ } | ||
doneFn, | ||
onTransmuxerLog | ||
onTransmuxerLog, | ||
triggerSegmentEventFn | ||
}); | ||
@@ -812,4 +880,8 @@ }; | ||
if (segment.map && segment.map.encryptedBytes && !segment.map.bytes) { | ||
return decrypt({ | ||
decryptionWorker, | ||
triggerSegmentEventFn({ type: 'segmentdecryptionstart', segment }); | ||
// add -init to the "id" to differentiate between segment | ||
// and init segment decryption, just in case they happen | ||
// at the same time at some point in the future. | ||
segment.requestId += '-init'; | ||
return decrypt({ decryptionWorker, | ||
// add -init to the "id" to differentiate between segment | ||
@@ -820,6 +892,7 @@ // and init segment decryption, just in case they happen | ||
encryptedBytes: segment.map.encryptedBytes, | ||
key: segment.map.key | ||
}, (decryptedBytes) => { | ||
key: segment.map.key, | ||
segment, | ||
doneFn }, (decryptedBytes) => { | ||
segment.map.bytes = decryptedBytes; | ||
triggerSegmentEventFn({ type: 'segmentdecryptioncomplete', segment }); | ||
parseInitSegment(segment, (parseError) => { | ||
@@ -997,3 +1070,4 @@ if (parseError) { | ||
doneFn, | ||
onTransmuxerLog | ||
onTransmuxerLog, | ||
triggerSegmentEventFn | ||
}) => { | ||
@@ -1014,3 +1088,4 @@ const activeXhrs = []; | ||
doneFn, | ||
onTransmuxerLog | ||
onTransmuxerLog, | ||
triggerSegmentEventFn | ||
}); | ||
@@ -1030,3 +1105,6 @@ | ||
}); | ||
const keyRequestCallback = handleKeyResponse(segment, objects, finishProcessingFn); | ||
const keyRequestCallback = handleKeyResponse(segment, objects, finishProcessingFn, triggerSegmentEventFn); | ||
const keyInfo = { uri: segment.key.resolvedUri }; | ||
triggerSegmentEventFn({ type: 'segmentkeyloadstart', segment, keyInfo }); | ||
const keyXhr = xhr(keyRequestOptions, keyRequestCallback); | ||
@@ -1047,3 +1125,6 @@ | ||
}); | ||
const mapKeyRequestCallback = handleKeyResponse(segment, [segment.map.key], finishProcessingFn); | ||
const mapKeyRequestCallback = handleKeyResponse(segment, [segment.map.key], finishProcessingFn, triggerSegmentEventFn); | ||
const keyInfo = { uri: segment.map.key.resolvedUri }; | ||
triggerSegmentEventFn({ type: 'segmentkeyloadstart', segment, keyInfo }); | ||
const mapKeyXhr = xhr(mapKeyRequestOptions, mapKeyRequestCallback); | ||
@@ -1059,3 +1140,5 @@ | ||
}); | ||
const initSegmentRequestCallback = handleInitSegmentResponse({segment, finishProcessingFn}); | ||
const initSegmentRequestCallback = handleInitSegmentResponse({segment, finishProcessingFn, triggerSegmentEventFn}); | ||
triggerSegmentEventFn({ type: 'segmentloadstart', segment }); | ||
const initSegmentXhr = xhr(initSegmentOptions, initSegmentRequestCallback); | ||
@@ -1076,4 +1159,7 @@ | ||
finishProcessingFn, | ||
responseType: segmentRequestOptions.responseType | ||
responseType: segmentRequestOptions.responseType, | ||
triggerSegmentEventFn | ||
}); | ||
triggerSegmentEventFn({ type: 'segmentloadstart', segment }); | ||
const segmentXhr = xhr(segmentRequestOptions, segmentRequestCallback); | ||
@@ -1080,0 +1166,0 @@ |
@@ -14,2 +14,4 @@ /** | ||
import logger from './util/logger'; | ||
import { createTimeRanges } from './util/vjs-compat'; | ||
import videojs from 'video.js'; | ||
@@ -28,3 +30,3 @@ // Set of events that reset the playback-watcher time check logic and clear the timeout | ||
*/ | ||
export default class PlaybackWatcher { | ||
export default class PlaybackWatcher extends videojs.EventTarget { | ||
/** | ||
@@ -37,2 +39,3 @@ * Represents an PlaybackWatcher object. | ||
constructor(options) { | ||
super(); | ||
this.playlistController_ = options.playlistController; | ||
@@ -44,2 +47,3 @@ this.tech_ = options.tech; | ||
this.media = options.media; | ||
this.playedRanges_ = []; | ||
@@ -212,2 +216,7 @@ this.consecutiveUpdates = 0; | ||
if (isBufferedDifferent) { | ||
const metadata = { | ||
bufferedRanges: buffered | ||
}; | ||
pc.trigger({ type: 'bufferedrangeschanged', metadata }); | ||
this.resetSegmentDownloads_(type); | ||
@@ -279,2 +288,8 @@ return; | ||
} else { | ||
this.playedRanges_.push(createTimeRanges([this.lastRecordedTime, currentTime])); | ||
const metadata = { | ||
playedRanges: this.playedRanges_ | ||
}; | ||
this.playlistController_.trigger({ type: 'playedrangeschanged', metadata }); | ||
this.consecutiveUpdates = 0; | ||
@@ -598,3 +613,10 @@ this.lastRecordedTime = currentTime; | ||
this.tech_.setCurrentTime(nextRange.start(0) + Ranges.TIME_FUDGE_FACTOR); | ||
const metadata = { | ||
gapInfo: { | ||
from: currentTime, | ||
to: nextRange.start(0) | ||
} | ||
}; | ||
this.playlistController_.trigger({type: 'gapjumped', metadata}); | ||
this.tech_.trigger({type: 'usage', name: 'vhs-gap-skip'}); | ||
@@ -601,0 +623,0 @@ } |
@@ -24,2 +24,3 @@ /** | ||
import DateRangesStorage from './util/date-ranges'; | ||
import { getStreamingNetworkErrorMetadata } from './error-codes.js'; | ||
@@ -375,2 +376,30 @@ const { EventTarget } = videojs; | ||
const playlistMetadataPayload = (playlists, type, isLive) => { | ||
if (!playlists) { | ||
return; | ||
} | ||
const renditions = []; | ||
playlists.forEach((playlist) => { | ||
// we need attributes to populate rendition data. | ||
if (!playlist.attributes) { | ||
return; | ||
} | ||
const { BANDWIDTH, RESOLUTION, CODECS } = playlist.attributes; | ||
renditions.push({ | ||
id: playlist.id, | ||
bandwidth: BANDWIDTH, | ||
resolution: RESOLUTION, | ||
codecs: CODECS | ||
}); | ||
}); | ||
return { | ||
type, | ||
isLive, | ||
renditions | ||
}; | ||
}; | ||
/** | ||
@@ -491,5 +520,3 @@ * Load a playlist from a remote location | ||
code: (xhr.status >= 500) ? 4 : 2, | ||
metadata: { | ||
errorType: videojs.Error.HlsPlaylistRequestError | ||
} | ||
metadata: getStreamingNetworkErrorMetadata({ requestType: xhr.requestType, request: xhr, error: xhr.error }) | ||
}; | ||
@@ -501,10 +528,18 @@ | ||
parseManifest_({url, manifestString}) { | ||
return parseManifest({ | ||
onwarn: ({message}) => this.logger_(`m3u8-parser warn for ${url}: ${message}`), | ||
oninfo: ({message}) => this.logger_(`m3u8-parser info for ${url}: ${message}`), | ||
manifestString, | ||
customTagParsers: this.customTagParsers, | ||
customTagMappers: this.customTagMappers, | ||
llhls: this.llhls | ||
}); | ||
try { | ||
return parseManifest({ | ||
onwarn: ({message}) => this.logger_(`m3u8-parser warn for ${url}: ${message}`), | ||
oninfo: ({message}) => this.logger_(`m3u8-parser info for ${url}: ${message}`), | ||
manifestString, | ||
customTagParsers: this.customTagParsers, | ||
customTagMappers: this.customTagMappers, | ||
llhls: this.llhls | ||
}); | ||
} catch (error) { | ||
this.error = error; | ||
this.error.metadata = { | ||
errorType: videojs.Error.StreamingHlsPlaylistParserError, | ||
error | ||
}; | ||
} | ||
} | ||
@@ -529,2 +564,10 @@ | ||
const metadata = { | ||
playlistInfo: { | ||
type: 'media', | ||
uri: url | ||
} | ||
}; | ||
this.trigger({type: 'playlistparsestart', metadata }); | ||
const playlist = playlistObject || this.parseManifest_({ | ||
@@ -558,3 +601,4 @@ url, | ||
this.updateMediaUpdateTimeout_(refreshDelay(this.media(), !!update)); | ||
metadata.parsedPlaylist = playlistMetadataPayload(this.main.playlists, metadata.playlistInfo.type, !this.media_.endList); | ||
this.trigger({ type: 'playlistparsecomplete', metadata }); | ||
this.trigger('loadedplaylist'); | ||
@@ -699,3 +743,11 @@ } | ||
this.pendingMedia_ = playlist; | ||
const metadata = { | ||
playlistInfo: { | ||
type: 'media', | ||
uri: playlist.uri | ||
} | ||
}; | ||
this.trigger({ type: 'playlistrequeststart', metadata }); | ||
this.request = this.vhs_.xhr({ | ||
@@ -719,2 +771,4 @@ uri: playlist.resolvedUri, | ||
this.trigger({ type: 'playlistrequestcomplete', metadata }); | ||
this.haveMetadata({ | ||
@@ -847,3 +901,10 @@ playlistString: req.responseText, | ||
} | ||
const metadata = { | ||
playlistInfo: { | ||
type: 'multivariant', | ||
uri: this.src | ||
} | ||
}; | ||
this.trigger({ type: 'playlistrequeststart', metadata }); | ||
// request the specified URL | ||
@@ -870,5 +931,3 @@ this.request = this.vhs_.xhr({ | ||
code: 2, | ||
metadata: { | ||
errorType: videojs.Error.HlsPlaylistRequestError | ||
} | ||
metadata: getStreamingNetworkErrorMetadata({ requestType: req.requestType, request: req, error }) | ||
}; | ||
@@ -880,5 +939,7 @@ if (this.state === 'HAVE_NOTHING') { | ||
} | ||
this.trigger({ type: 'playlistrequestcomplete', metadata }); | ||
this.src = resolveManifestRedirect(this.src, req); | ||
this.trigger({ type: 'playlistparsestart', metadata }); | ||
const manifest = this.parseManifest_({ | ||
@@ -889,2 +950,6 @@ manifestString: req.responseText, | ||
// we haven't loaded any variant playlists here so we default to false for isLive. | ||
metadata.parsedPlaylist = playlistMetadataPayload(manifest.playlists, metadata.playlistInfo.type, false); | ||
this.trigger({ type: 'playlistparsecomplete', metadata }); | ||
this.setupInitialPlaylist(manifest); | ||
@@ -891,0 +956,0 @@ }); |
@@ -30,2 +30,11 @@ import { isIncompatible, isEnabled, isAudioOnly } from './playlist.js'; | ||
} | ||
const metadata = { | ||
renditionInfo: { | ||
id: playlistID, | ||
bandwidth: playlist.attributes.BANDWIDTH, | ||
resolution: playlist.attributes.RESOLUTION, | ||
codecs: playlist.attributes.CODECS | ||
}, | ||
cause: 'fast-quality' | ||
}; | ||
@@ -36,5 +45,5 @@ if (enable !== currentlyEnabled && !incompatible) { | ||
if (enable) { | ||
loader.trigger('renditionenabled'); | ||
loader.trigger({ type: 'renditionenabled', metadata}); | ||
} else { | ||
loader.trigger('renditiondisabled'); | ||
loader.trigger({ type: 'renditiondisabled', metadata}); | ||
} | ||
@@ -41,0 +50,0 @@ } |
import TransmuxWorker from 'worker!./transmuxer-worker.js'; | ||
import videojs from 'video.js'; | ||
import { segmentInfoPayload } from './segment-loader'; | ||
@@ -85,3 +87,5 @@ export const handleData_ = (event, transmuxedData, callback) => { | ||
onTransmuxerLog, | ||
isEndOfTimeline | ||
isEndOfTimeline, | ||
segment, | ||
triggerSegmentEventFn | ||
} = options; | ||
@@ -157,4 +161,16 @@ const transmuxedData = { | ||
}; | ||
const handleError = () => { | ||
const error = { | ||
message: 'Received an error message from the transmuxer worker', | ||
metadata: { | ||
errorType: videojs.Error.StreamingFailedToTransmuxSegment, | ||
segmentInfo: segmentInfoPayload({segment}) | ||
} | ||
}; | ||
onDone(null, error); | ||
}; | ||
transmuxer.onmessage = handleMessage; | ||
transmuxer.onerror = handleError; | ||
@@ -187,2 +203,3 @@ if (audioAppendStart) { | ||
triggerSegmentEventFn({ type: 'segmenttransmuxingstart', segment }); | ||
transmuxer.postMessage( | ||
@@ -189,0 +206,0 @@ { |
@@ -287,4 +287,11 @@ /** | ||
} | ||
const metadata = { | ||
codecsChangeInfo: { | ||
from: oldCodec, | ||
to: codec | ||
} | ||
}; | ||
sourceUpdater.logger_(`changing ${type}Buffer codec from ${sourceUpdater.codecs[type]} to ${codec}`); | ||
sourceUpdater.trigger({ type: 'codecschange', metadata }); | ||
sourceUpdater.logger_(`changing ${type}Buffer codec from ${oldCodec} to ${codec}`); | ||
@@ -296,2 +303,7 @@ // check if change to the provided type is supported | ||
} catch (e) { | ||
metadata.errorType = videojs.Error.StreamingCodecsChangeError; | ||
metadata.error = e; | ||
e.metadata = metadata; | ||
sourceUpdater.error_ = e; | ||
sourceUpdater.trigger('error'); | ||
videojs.log.warn(`Failed to changeType on ${type}Buffer`, e); | ||
@@ -298,0 +310,0 @@ } |
@@ -37,3 +37,10 @@ import videojs from 'video.js'; | ||
delete this.pendingTimelineChanges_[type]; | ||
this.trigger('timelinechange'); | ||
const metadata = { | ||
timelineChangeInfo: { | ||
from, | ||
to | ||
} | ||
}; | ||
this.trigger({ type: 'timelinechange', metadata }); | ||
} | ||
@@ -40,0 +47,0 @@ return this.lastTimelineChanges_[type]; |
@@ -5,2 +5,3 @@ import {getId3Offset} from '@videojs/vhs-utils/es/id3-helpers'; | ||
import {callbackWrapper} from '../xhr'; | ||
import { getStreamingNetworkErrorMetadata } from '../error-codes'; | ||
@@ -16,3 +17,3 @@ // calls back if the request is readyState DONE | ||
const containerRequest = (uri, xhr, cb) => { | ||
const containerRequest = (uri, xhr, cb, requestType) => { | ||
let bytes = []; | ||
@@ -33,2 +34,3 @@ let id3Offset; | ||
if (error) { | ||
error.metadata = getStreamingNetworkErrorMetadata({ requestType, request, error }); | ||
return endRequestAndCallback(error, request, '', bytes); | ||
@@ -35,0 +37,0 @@ } |
@@ -798,2 +798,4 @@ /** | ||
}; | ||
// pass player to allow for player level eventing on construction. | ||
this.options_.player_ = this.player_; | ||
@@ -816,2 +818,3 @@ this.playlistController_ = new PlaylistController(this.options_); | ||
this.attachStreamingEventListeners_(); | ||
this.playlistController_.on('error', () => { | ||
@@ -1101,6 +1104,3 @@ const player = videojs.players[this.tech_.options_.playerId]; | ||
message: 'Failed to initialize media keys for EME', | ||
code: 3, | ||
metadata: { | ||
errorType: videojs.Error.EMEKeySessionCreationError | ||
} | ||
code: 3 | ||
}); | ||
@@ -1332,2 +1332,30 @@ }); | ||
} | ||
attachStreamingEventListeners_() { | ||
const playlistControllerEvents = [ | ||
'seekablerangeschanged', | ||
'bufferedrangeschanged', | ||
'contentsteeringloadstart', | ||
'contentsteeringloadcomplete', | ||
'contentsteeringparsed' | ||
]; | ||
const playbackWatcher = [ | ||
'gapjumped', | ||
'playedrangeschanged' | ||
]; | ||
// re-emit streaming events and payloads on the player. | ||
playlistControllerEvents.forEach((eventName) => { | ||
this.playlistController_.on(eventName, (metadata) => { | ||
this.player_.trigger({...metadata}); | ||
}); | ||
}); | ||
playbackWatcher.forEach((eventName) => { | ||
this.playbackWatcher_.on(eventName, (metadata) => { | ||
this.player_.trigger({...metadata}); | ||
}); | ||
}); | ||
} | ||
} | ||
@@ -1334,0 +1362,0 @@ |
@@ -316,6 +316,3 @@ /** | ||
() => this.stopForError({ | ||
message: 'Error loading vtt.js', | ||
metadata: { | ||
errorType: videojs.Error.VttLoadError | ||
} | ||
message: 'Error loading vtt.js' | ||
}) | ||
@@ -334,3 +331,4 @@ ); | ||
metadata: { | ||
errorType: videojs.Error.VttCueParsingError | ||
errorType: videojs.Error.StreamingVttParserError, | ||
error: e | ||
} | ||
@@ -337,0 +335,0 @@ }); |
@@ -135,2 +135,3 @@ /** | ||
request.uri = options.uri; | ||
request.requestType = options.requestType; | ||
request.requestTime = Date.now(); | ||
@@ -137,0 +138,0 @@ return request; |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
6755948
133162