Skip to content

Commit

Permalink
feat(FEC-11761): expose stream timed metadata - phase 2 (#623)
Browse files Browse the repository at this point in the history
  • Loading branch information
yairans committed Jan 12, 2022
1 parent 3a32cd7 commit 7812357
Show file tree
Hide file tree
Showing 11 changed files with 286 additions and 75 deletions.
54 changes: 37 additions & 17 deletions src/engines/html5/html5.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import VideoTrack from '../../track/video-track';
import AudioTrack from '../../track/audio-track';
import PKTextTrack, {getActiveCues} from '../../track/text-track';
import ImageTrack from '../../track/image-track';
import {Cue} from '../../track/vtt-cue';
import {createTimedMetadata} from '../../track/timed-metadata';
import * as Utils from '../../utils/util';
import Html5AutoPlayCapability from './capabilities/html5-autoplay';
import Error from '../../error/error';
Expand Down Expand Up @@ -1163,27 +1165,45 @@ export default class Html5 extends FakeEventTarget implements IEngine {
}

_handleMetadataTrackEvents(): void {
const listenToCueChange = track => {
track.mode = PKTextTrack.MODE.HIDDEN;
this._eventManager.listen(track, 'cuechange', () => {
this.dispatchEvent(new FakeEvent(CustomEventType.TIMED_METADATA, {cues: Array.from(track.activeCues), label: track.label}));
const listenToCueChange = metadataTrack => {
metadataTrack.mode = PKTextTrack.MODE.HIDDEN;
this._eventManager.listen(metadataTrack, 'cuechange', () => {
let activeCues = [];
Array.from(this._el.textTracks).forEach((track: TextTrack) => {
if (PKTextTrack.isMetaDataTrack(track)) {
activeCues = activeCues.concat(getActiveCues(track.activeCues));
}
});
activeCues = activeCues.sort((a: Cue, b: Cue) => {
return a.startTime - b.startTime;
});
this.dispatchEvent(new FakeEvent(CustomEventType.TIMED_METADATA, {cues: activeCues}));
this.dispatchEvent(
new FakeEvent(CustomEventType.TIMED_METADATA_CHANGE, {
cues: activeCues.map(cue => createTimedMetadata(cue))
})
);
});
};
const metadataTrack = Array.from(this._el.textTracks).find((track: TextTrack) => PKTextTrack.isMetaDataTrack(track));
if (metadataTrack) {
listenToCueChange(metadataTrack);
} else {
this._eventManager.listen(this._el.textTracks, 'addtrack', (event: any) => {
if (PKTextTrack.isMetaDataTrack(event.track)) {
listenToCueChange(event.track);

Array.from(this._el.textTracks).forEach((track: TextTrack) => {
if (PKTextTrack.isMetaDataTrack(track)) {
listenToCueChange(track);
}
});

this._eventManager.listen(this._el.textTracks, 'addtrack', (event: any) => {
if (PKTextTrack.isMetaDataTrack(event.track)) {
listenToCueChange(event.track);
}
});

this._eventManager.listen(this._el.textTracks, 'change', () => {
Array.from(this._el.textTracks).forEach((track: TextTrack) => {
if (PKTextTrack.isMetaDataTrack(track) && track.mode !== PKTextTrack.MODE.HIDDEN) {
track.mode = PKTextTrack.MODE.HIDDEN;
}
});
}
this._eventManager.listen(this._el.textTracks, 'change', () => {
const metadataTrack = Array.from(this._el.textTracks).find((track: TextTrack) => PKTextTrack.isMetaDataTrack(track));
if (metadataTrack && metadataTrack.mode !== PKTextTrack.MODE.HIDDEN) {
metadataTrack.mode = PKTextTrack.MODE.HIDDEN;
}
});
}

Expand Down
4 changes: 4 additions & 0 deletions src/event/event-type.js
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,10 @@ const CustomEventType: PKEventTypes = {
* Fired when the timed metadata triggered
*/
TIMED_METADATA: 'timedmetadata',
/**
* Fired when the timed metadata triggered
*/
TIMED_METADATA_CHANGE: 'timedmetadatachange',
/**
* Fires when new timed metadata added
*/
Expand Down
1 change: 1 addition & 0 deletions src/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -1921,6 +1921,7 @@ export default class Player extends FakeEventTarget {
this._eventManager.listen(this._engine, CustomEventType.TEXT_CUE_CHANGED, (event: FakeEvent) => this._onCueChange(event));
this._eventManager.listen(this._engine, CustomEventType.ABR_MODE_CHANGED, (event: FakeEvent) => this.dispatchEvent(event));
this._eventManager.listen(this._engine, CustomEventType.TIMED_METADATA, (event: FakeEvent) => this.dispatchEvent(event));
this._eventManager.listen(this._engine, CustomEventType.TIMED_METADATA_CHANGE, (event: FakeEvent) => this.dispatchEvent(event));
this._eventManager.listen(this._engine, CustomEventType.TIMED_METADATA_ADDED, (event: FakeEvent) => this.dispatchEvent(event));
this._eventManager.listen(this._engine, CustomEventType.PLAY_FAILED, (event: FakeEvent) => {
this._onPlayFailed(event);
Expand Down
6 changes: 3 additions & 3 deletions src/playkit.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import ImageTrack from './track/image-track';
import VideoTrack from './track/video-track';
import AudioTrack from './track/audio-track';
import TextTrack from './track/text-track';
import {CuePoint, createTextTrackCue} from './track/cue-point';
import {TimedMetadata, createTextTrackCue, createTimedMetadata} from './track/timed-metadata';
import TextStyle from './track/text-style';
import {Cue} from './track/vtt-cue';
import Env from './utils/env';
Expand Down Expand Up @@ -63,8 +63,8 @@ export {BaseMiddleware};
// Export the tracks classes
export {Track, VideoTrack, AudioTrack, TextTrack, ImageTrack, TextStyle, Cue};

// Export the cue point class and function
export {CuePoint, createTextTrackCue};
// Export the timed metadata class and function
export {TimedMetadata, createTextTrackCue, createTimedMetadata};

// Export utils library
export {Utils};
Expand Down
53 changes: 0 additions & 53 deletions src/track/cue-point.js

This file was deleted.

4 changes: 2 additions & 2 deletions src/track/text-track.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,11 @@ TextTrack.isExternalTrack = (track: any) => {
* @returns {void}
* @private
*/
function getActiveCues(textTrackCueList: TextTrackCueList) {
function getActiveCues(textTrackCueList: TextTrackCueList): Array<Cue> {
let normalizedCues: Array<Cue> = [];
for (let cue of textTrackCueList) {
//Normalize cues to be of type of VTT model
if (window.VTTCue && cue instanceof window.VTTCue) {
if ((window.VTTCue && cue instanceof window.VTTCue) || (window.DataCue && cue instanceof window.DataCue)) {
normalizedCues.push(cue);
} else if (window.TextTrackCue && cue instanceof window.TextTrackCue) {
try {
Expand Down
93 changes: 93 additions & 0 deletions src/track/timed-metadata.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
//@flow
class TimedMetadata {
static TYPE: {[type: string]: string};

startTime: number;
endTime: number;
id: string;
type: string;
metadata: string | Object;
/**
* @constructor
* @param {number} startTime - start time.
* @param {number} endTime - end time.
* @param {string} id - id.
* @param {string} type - type.
* @param {any} metadata - metadata.
*/
constructor(startTime: number, endTime: number, id: string, type: string, metadata: any) {
this.startTime = startTime;
this.endTime = endTime;
this.id = id;
this.type = type;
this.metadata = metadata;
}
}

TimedMetadata.TYPE = {
ID3: 'id3',
EMSG: 'emsg',
CUE_POINT: 'cuepoint',
CUSTOM: 'custom'
};

/**
* Create a standard TextTrackCue.
* @param {TimedMetadata} timedMetadata - timed metadata object.
* @returns {TextTrackCue} - the created text track cue
* @private
*/
function createTextTrackCue(timedMetadata: TimedMetadata): TextTrackCue {
const {startTime, endTime, id, type, metadata} = timedMetadata;
let cue = {};
if (window.VTTCue) {
cue = new window.VTTCue(startTime, endTime, '');
} else if (window.TextTrackCue) {
// IE11 support
cue = new window.TextTrackCue(startTime, endTime, '');
}
const cueValue = {key: type, data: metadata};
cue.id = id;
cue.value = cueValue;
return cue;
}

/**
* Create a timed metadata object from a standard TextTrackCue.
* @param {TextTrackCue} cue - text track cue.
* @returns {?TimedMetadata} - the created timed metadata object.
* @private
*/
function createTimedMetadata(cue: TextTrackCue): ?TimedMetadata {
if (cue) {
const {startTime, endTime, id} = cue;
const {type, metadata} = _getTypeAndMetadata(cue);
return new TimedMetadata(startTime, endTime, id, type, metadata);
}
return null;
}

/**
* @param {TextTrackCue} cue - cue
* @return {Object} - type and data
* @private
*/
function _getTypeAndMetadata(cue: TextTrackCue): Object {
const {
type,
value,
track: {label}
} = cue;
const {key, data} = value;
const isId3 = type === 'org.id3' || label === 'id3';
let timedMetadataType = Object.values(TimedMetadata.TYPE).find(type => type === key);
if (!timedMetadataType) {
timedMetadataType = isId3 ? TimedMetadata.TYPE.ID3 : TimedMetadata.TYPE.CUSTOM;
}
return {
type: timedMetadataType,
metadata: isId3 ? value : data
};
}

export {TimedMetadata, createTextTrackCue, createTimedMetadata};
26 changes: 26 additions & 0 deletions src/utils/binary-search.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//@flow
/**
* @param {Array<any>} list The array to search.
* @param {Function} comparisonFn
* Called and provided a candidate item as the first argument.
* Should return:
* > -1 if the item should be located at a lower index than the provided item.
* > 1 if the item should be located at a higher index than the provided item.
* > 0 if the item is the item you're looking for.
*
* @return {any} The object if it is found or null otherwise.
*/
export function binarySearch(list: Array<any> = [], comparisonFn: Function = () => 1): any {
if (list.length === 0 || (list.length === 1 && comparisonFn(list[0]) !== 0)) {
return null;
}
const mid = Math.floor(list.length / 2);
if (comparisonFn(list[mid]) === 0) {
return list[mid];
}
if (comparisonFn(list[mid]) > 0) {
return binarySearch(list.slice(0, mid), comparisonFn);
} else {
return binarySearch(list.slice(mid + 1), comparisonFn);
}
}
1 change: 1 addition & 0 deletions src/utils/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './util';
export {ResizeWatcher} from './resize-watcher';
export {MultiMap} from './multi-map';
export {binarySearch} from './binary-search';
Loading

0 comments on commit 7812357

Please sign in to comment.