Skip to content
Open
Show file tree
Hide file tree
Changes from 18 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/playback/src/lib/consts/text-tracks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const Thumbnails = 'thumbnails';
31 changes: 31 additions & 0 deletions packages/playback/src/lib/models/player-text-tracks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import type { IPlayerTextTrack } from '../types/text-track.declarations';

export class PlayerTextTrack implements IPlayerTextTrack {
public readonly id: string;
public readonly activeCues: TextTrackCueList | null;
public readonly cues: TextTrackCueList | null;
public readonly kind: TextTrackKind;
public readonly label: string;
public readonly language: string;
public readonly mode: string;

public constructor(textTrack: TextTrack) {
this.id = textTrack.id;
this.activeCues = textTrack.activeCues;
this.cues = textTrack.cues;
this.kind = textTrack.kind as TextTrackKind;
this.label = textTrack.label;
this.language = textTrack.language;
this.mode = textTrack.mode;
}

public static fromTextTracks(textTrackList: TextTrackList): Array<PlayerTextTrack> {
const playerTextTracks = [];
for (let i = 0; i < textTrackList.length; i++) {
if (textTrackList[i].kind !== 'metadata') {
playerTextTracks.push(new PlayerTextTrack(textTrackList[i]));
}
}
return playerTextTracks;
}
}
22 changes: 22 additions & 0 deletions packages/playback/src/lib/models/player-thumbnail-tracks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Thumbnails } from '../consts/text-tracks';
import type { IPlayerThumbnailTrack } from '../types/thumbnail-track.declarations';
import { PlayerTextTrack } from './player-text-tracks';

export class PlayerThumbnailTrack extends PlayerTextTrack implements IPlayerThumbnailTrack {
public readonly isActive: boolean;

public constructor(textTrack: TextTrack) {
super(textTrack);
this.isActive = this.mode !== 'disabled';
}

public static fromTextTracks(textTrackList: TextTrackList): Array<PlayerThumbnailTrack> {
const playerThumbnailTracks = [];
for (let i = 0; i < textTrackList.length; i++) {
if (textTrackList[i].label.startsWith(Thumbnails)) {
playerThumbnailTracks.push(new PlayerThumbnailTrack(textTrackList[i]));
}
}
return playerThumbnailTracks;
}
}
7 changes: 3 additions & 4 deletions packages/playback/src/lib/pipelines/base-pipeline.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { IPipeline, IPipelineDependencies } from '../types/pipeline.declarations';
import { PlayerTimeRange } from '../models/player-time-range';
import type { PlaybackState } from '../consts/playback-state';
import type { IPlaybackStats } from '../types/playback-stats.declarations';
import type { INetworkManager } from '../types/network.declarations';
import type { IQualityLevel } from '../types/quality-level.declarations';
Expand Down Expand Up @@ -46,8 +45,6 @@ export abstract class BasePipeline implements IPipeline {

public abstract getThumbnailTracks(): Array<IPlayerThumbnailTrack>;

public abstract getPlaybackState(): PlaybackState;

public abstract start(): void;

public getPlaybackStats(): IPlaybackStats {
Expand All @@ -71,7 +68,9 @@ export abstract class BasePipeline implements IPipeline {
}

public play(): void {
void this.videoElement_.play();
void this.videoElement_.play().catch((error) => {
this.logger_.error(error);
});
}

public seek(seekTarget: number): boolean {
Expand Down
71 changes: 54 additions & 17 deletions packages/playback/src/lib/pipelines/native/native-pipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,43 +3,72 @@ import type { IPipelineDependencies } from '../../types/pipeline.declarations';
import type { IPlayerAudioTrack } from '../../types/audio-track.declarations';
import { PlayerAudioTrack } from '../../models/player-audio-track';
import type { IQualityLevel } from '../../types/quality-level.declarations';
import type { PlaybackState } from 'src/lib/consts/playback-state';
import type { IPlayerTextTrack } from 'src/lib/types/text-track.declarations';
import type {
IRemoteVttThumbnailTrackOptions,
IPlayerThumbnailTrack,
} from 'src/lib/types/thumbnail-track.declarations';
import type { IPlayerTextTrack } from '../../types/text-track.declarations';
import {
type IRemoteVttThumbnailTrackOptions,
type IPlayerThumbnailTrack,
} from '../../types/thumbnail-track.declarations';
import { PlayerTextTrack } from '../../models/player-text-tracks';
import { PlayerThumbnailTrack } from '../../models/player-thumbnail-tracks';
import { Thumbnails } from '../../consts/text-tracks';

export class NativePipeline extends BasePipeline {
private constructor(dependencies: IPipelineDependencies) {
super(dependencies);
}

public static create(dependencies: IPipelineDependencies): NativePipeline {
dependencies.logger = dependencies.logger.createSubLogger('NativePipeline');

return new NativePipeline(dependencies);
}

public getTextTracks(): Array<IPlayerTextTrack> {
throw new Error('Method not implemented.');
if (this.videoElement_.textTracks) {
return PlayerTextTrack.fromTextTracks(this.videoElement_.textTracks);
}
return [];
}

// eslint-disable-next-line no-unused-vars,@typescript-eslint/no-unused-vars
public removeRemoteThumbnailTrack(id: string): boolean {
throw new Error('Method not implemented.');
if (this.videoElement_.textTracks) {
const trackToRemove = this.videoElement_.textTracks.getTrackById(id);
Comment thread
adrums86 marked this conversation as resolved.

// disable native track since there is no Track element to remove.
if (trackToRemove && trackToRemove.label.startsWith(Thumbnails)) {
trackToRemove.mode = 'disabled';
return true;
}
}
return false;
}

// eslint-disable-next-line no-unused-vars,@typescript-eslint/no-unused-vars
public addRemoteVttThumbnailTrack(options: IRemoteVttThumbnailTrackOptions): boolean {
throw new Error('Method not implemented.');
// TODO: Request and parse thumbnails from VTT file or manifest.
if (options.url) {
this.videoElement_.addTextTrack('metadata', Thumbnails);
return true;
}
return false;
}

// eslint-disable-next-line no-unused-vars,@typescript-eslint/no-unused-vars
public selectThumbnailTrack(id: string): boolean {
throw new Error('Method not implemented.');
if (this.videoElement_.textTracks) {
const trackToSelect = this.videoElement_.textTracks.getTrackById(id);

if (trackToSelect && trackToSelect.label.startsWith(Thumbnails)) {
trackToSelect.mode = 'hidden';
return true;
}
}
return false;
}

public getThumbnailTracks(): Array<IPlayerThumbnailTrack> {
throw new Error('Method not implemented.');
}
public getPlaybackState(): PlaybackState {
throw new Error('Method not implemented.');
if (this.videoElement_.textTracks) {
return PlayerThumbnailTrack.fromTextTracks(this.videoElement_.textTracks);
}
return [];
}

public getAudioTracks(): Array<IPlayerAudioTrack> {
Expand Down Expand Up @@ -95,4 +124,12 @@ export class NativePipeline extends BasePipeline {
this.videoElement_.removeAttribute('src');
this.videoElement_.load();
}

public play(): void {
super.play();
}

public pause(): void {
super.pause();
}
}
8 changes: 8 additions & 0 deletions packages/playback/src/lib/player/base-player.ts
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,7 @@ export abstract class BasePlayer {
this.activeVideoElement_.addEventListener('volumechange', this.handleVolumeChange_);
this.activeVideoElement_.addEventListener('ratechange', this.handleRateChange_);
this.activeVideoElement_.addEventListener('timeupdate', this.handleTimeUpdate_);
this.activeVideoElement_.addEventListener('waiting', this.handleWaiting_);
// EME
this.activeVideoElement_.addEventListener('encrypted', this.handleEncryptedEvent_);
this.activeVideoElement_.addEventListener('waitingforkey', this.handleWaitingForKeyEvent_);
Expand Down Expand Up @@ -428,6 +429,7 @@ export abstract class BasePlayer {
this.activeVideoElement_.removeEventListener('volumechange', this.handleVolumeChange_);
this.activeVideoElement_.removeEventListener('ratechange', this.handleRateChange_);
this.activeVideoElement_.removeEventListener('timeupdate', this.handleTimeUpdate_);
this.activeVideoElement_.removeEventListener('waiting', this.handleWaiting_);
// EME
this.activeVideoElement_.removeEventListener('encrypted', this.handleEncryptedEvent_);
this.activeVideoElement_.removeEventListener('waitingforkey', this.handleWaitingForKeyEvent_);
Expand Down Expand Up @@ -575,6 +577,10 @@ export abstract class BasePlayer {
this.eventEmitter_.emitEvent(new CurrentTimeChangedEvent(target.currentTime));
};

protected readonly handleWaiting_ = (): void => {
this.transitionPlaybackState_(PlaybackState.Buffering);
};

protected readonly handleEncryptedEvent_ = (event: MediaEncryptedEvent): void => {
this.logger_.debug('received encrypted event', event);

Expand Down Expand Up @@ -621,6 +627,7 @@ export abstract class BasePlayer {
*/
public play(): void {
this.activeVideoElement_?.play();
this.transitionPlaybackState_(PlaybackState.Playing);
// TODO: pass command to loader/pipeline
}

Expand All @@ -629,6 +636,7 @@ export abstract class BasePlayer {
*/
public pause(): void {
this.activeVideoElement_?.pause();
this.transitionPlaybackState_(PlaybackState.Paused);
// TODO: pass command to loader/pipeline
}

Expand Down
2 changes: 0 additions & 2 deletions packages/playback/src/lib/types/pipeline.declarations.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import type { PlaybackState } from '../consts/playback-state';
import type { IPlaybackStats } from './playback-stats.declarations';
import type { ILogger } from './logger.declarations';
import type { INetworkManager } from './network.declarations';
Expand Down Expand Up @@ -47,7 +46,6 @@ export interface IPipelineFactoryConfiguration {
export interface IPipeline {
play(): void;
pause(): void;
getPlaybackState(): PlaybackState;
seek(seekTarget: number): boolean;
getSeekableRanges(): Array<IPlayerTimeRange>;
getBufferedRanges(): Array<IPlayerTimeRange>;
Expand Down
8 changes: 7 additions & 1 deletion packages/playback/src/lib/types/text-track.declarations.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
export interface IPlayerTextTrack {
id: string;
readonly id: string;
readonly activeCues: TextTrackCueList | null;
readonly cues: TextTrackCueList | null;
readonly kind: TextTrackKind;
readonly label: string;
readonly language: string;
readonly mode: string;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export interface IPlayerThumbnailTrack {
id: string;
import type { IPlayerTextTrack } from './text-track.declarations';

export interface IPlayerThumbnailTrack extends IPlayerTextTrack {
isActive: boolean;
}

Expand Down
Loading