Skip to content

Commit

Permalink
Merge pull request #717 from videogular/feat/quality-switcher
Browse files Browse the repository at this point in the history
feat(controls): Add quality switcher
  • Loading branch information
Elecash authored Apr 18, 2018
2 parents eadfc31 + 0275a7b commit 09a0f1f
Show file tree
Hide file tree
Showing 5 changed files with 257 additions and 6 deletions.
9 changes: 6 additions & 3 deletions src/controls/controls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import { VgScrubBarCuePoints } from './vg-scrub-bar/vg-scrub-bar-cue-points/vg-s
import { VgScrubBarCurrentTime } from './vg-scrub-bar/vg-scrub-bar-current-time/vg-scrub-bar-current-time';
import { VgTimeDisplay, VgUtcPipe } from './vg-time-display/vg-time-display';
import { VgTrackSelector } from './vg-track-selector/vg-track-selector';
import { VgControlsHidden } from './../core/services/vg-controls-hidden';
import { VgControlsHidden } from '../core/services/vg-controls-hidden';
import { VgQualitySelector } from './vg-quality-selector/vg-quality-selector';

@NgModule({
imports: [ CommonModule ],
Expand All @@ -29,7 +30,8 @@ import { VgControlsHidden } from './../core/services/vg-controls-hidden';
VgScrubBarCurrentTime,
VgTimeDisplay,
VgUtcPipe,
VgTrackSelector
VgTrackSelector,
VgQualitySelector
],
exports: [
VgControls,
Expand All @@ -44,7 +46,8 @@ import { VgControlsHidden } from './../core/services/vg-controls-hidden';
VgScrubBarCurrentTime,
VgTimeDisplay,
VgUtcPipe,
VgTrackSelector
VgTrackSelector,
VgQualitySelector
],
providers: [ VgControlsHidden ]
})
Expand Down
22 changes: 22 additions & 0 deletions src/controls/vg-quality-selector/vg-quality-selector.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { VgQualitySelector } from "./vg-quality-selector";
import { VgAPI } from "../../core/services/vg-api";
import { ElementRef } from "@angular/core";

describe('Quality Selector control', () => {
let vgQualitySelector: VgQualitySelector;

beforeEach(() => {
const ref: ElementRef = {
nativeElement: {
getAttribute: (name) => {
return name;
}
}
};
vgQualitySelector = new VgQualitySelector(ref, new VgAPI());
});

describe('onPlayerReady', () => {

});
});
142 changes: 142 additions & 0 deletions src/controls/vg-quality-selector/vg-quality-selector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import {
Component,
ElementRef,
OnInit,
Input,
ViewEncapsulation,
OnDestroy,
SimpleChanges,
OnChanges, Output, EventEmitter
} from '@angular/core';
import { VgAPI } from '../../core/services/vg-api';
import { Subscription } from 'rxjs/Subscription';
import { BitrateOption } from '../../core/core';

@Component({
selector: 'vg-quality-selector',
encapsulation: ViewEncapsulation.None,
template: `
<div class="container">
<div class="quality-selected"
[class.vg-icon-closed_caption]="!bitrateSelected">
{{ bitrateSelected?.label }}
</div>
<select class="quality-selector"
(change)="selectBitrate($event.target.value)"
tabindex="0"
aria-label="quality selector"
[attr.aria-valuetext]="ariaValue">
<option
*ngFor="let bitrate of bitrates"
[value]="bitrate.qualityIndex"
[selected]="bitrate.qualityIndex === bitrateSelected.qualityIndex">
{{ bitrate.label }}
</option>
</select>
</div>
`,
styles: [ `
vg-quality-selector {
-webkit-touch-callout: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
display: flex;
justify-content: center;
width: 50px;
height: 50px;
cursor: pointer;
color: white;
line-height: 50px;
}
vg-quality-selector .container {
position: relative;
display: flex;
flex-grow: 1;
align-items: center;
padding: 0;
margin: 5px;
}
vg-quality-selector select.quality-selector {
width: 50px;
padding: 5px 8px;
border: none;
background: none;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
color: transparent;
font-size: 16px;
}
vg-quality-selector select.quality-selector::-ms-expand {
display: none;
}
vg-quality-selector select.quality-selector option {
color: #000;
}
vg-quality-selector .quality-selected {
position: absolute;
width: 100%;
height: 50px;
top: -6px;
text-align: center;
text-transform: uppercase;
font-family: Helvetica Neue, Helvetica, Arial, sans-serif;
padding-top: 2px;
pointer-events: none;
}
vg-quality-selector .vg-icon-closed_caption:before {
width: 100%;
}
` ]
})
export class VgQualitySelector implements OnInit, OnChanges, OnDestroy {
@Input() vgFor: string;
@Input() bitrates: BitrateOption[];

@Output() onBitrateChange: EventEmitter<BitrateOption> = new EventEmitter();

bitrateSelected: BitrateOption;

elem: HTMLElement;
target: any;

subscriptions: Subscription[] = [];

ariaValue: string;

constructor(ref: ElementRef, public API: VgAPI) {
this.elem = ref.nativeElement;
}

ngOnInit() {
if (this.API.isPlayerReady) {
this.onPlayerReady();
}
else {
this.subscriptions.push(this.API.playerReadyEvent.subscribe(() => this.onPlayerReady()));
}
}

ngOnChanges(changes: SimpleChanges) {
if (changes['bitrates'].currentValue && changes['bitrates'].currentValue.length) {
this.bitrates.forEach(item => item.label = item.label || Math.round(item.bitrate / 1000).toString())
}
}

onPlayerReady() {
this.target = this.API.getMediaById(this.vgFor);
}

selectBitrate(index: number) {
this.bitrateSelected = this.bitrates[index];
this.onBitrateChange.emit(this.bitrates[index]);
}

ngOnDestroy() {
this.subscriptions.forEach(s => s.unsubscribe());
}
}
9 changes: 9 additions & 0 deletions src/core/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@ export * from './vg-media/i-media-element';
// utility classes
export * from './vg-media/vg-media-element';

export interface BitrateOption {
qualityIndex: number;
width: number;
height: number;
bitrate: number;
mediaType: string;
label?: string;
}

/**
* @internal
*/
Expand Down
81 changes: 78 additions & 3 deletions src/streaming/vg-dash/vg-dash.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,33 @@
import { Directive, ElementRef, Input, SimpleChanges, OnChanges, OnDestroy, OnInit } from "@angular/core";
import {
Directive,
ElementRef,
Input,
SimpleChanges,
OnChanges,
OnDestroy,
OnInit,
Output,
EventEmitter
} from "@angular/core";
import { VgAPI } from '../../core/services/vg-api';
import { Subscription } from 'rxjs/Subscription';
import { IDRMLicenseServer } from '../streaming';
import { BitrateOption } from '../../core/core';

declare let dashjs;

@Directive({
selector: '[vgDash]'
selector: '[vgDash]',
exportAs: 'vgDash'
})
export class VgDASH implements OnInit, OnChanges, OnDestroy {
@Input() vgDash:string;
@Input() vgDRMToken:string;
@Input() vgDRMLicenseServer:IDRMLicenseServer;
@Input() vgDashBitrate: BitrateOption;
@Input() vgDashAuto = true;

@Output() onGetBitrates: EventEmitter<BitrateOption[]> = new EventEmitter();

vgFor: string;
target: any;
Expand All @@ -37,12 +53,16 @@ export class VgDASH implements OnInit, OnChanges, OnDestroy {
}

ngOnChanges(changes:SimpleChanges) {
if (changes['vgDash'].currentValue) {
if (changes['vgDash'] && changes['vgDash'].currentValue) {
this.createPlayer();
}
else {
this.destroyPlayer();
}

if (changes['vgDashBitrate'] && changes['vgDashBitrate'].currentValue) {
this.setBitrate(changes['vgDashBitrate'].currentValue);
}
}

createPlayer() {
Expand Down Expand Up @@ -72,6 +92,43 @@ export class VgDASH implements OnInit, OnChanges, OnDestroy {
this.dash.initialize(this.ref.nativeElement);
this.dash.setAutoPlay(false);

this.dash.on(dashjs.MediaPlayer.events.STREAM_INITIALIZED, () => {
const audioList = this.dash.getBitrateInfoListFor('audio');
const videoList = this.dash.getBitrateInfoListFor('video');

if (audioList.length > 1) {
if (this.vgDashAuto) {
audioList.forEach(item => item.qualityIndex = ++item.qualityIndex);
audioList.unshift({
qualityIndex: 0,
width: 0,
height: 0,
bitrate: 0,
mediaType: 'video',
label: 'AUTO'
});
}

this.onGetBitrates.emit(audioList);
}

if (videoList.length > 1) {
if (this.vgDashAuto) {
videoList.forEach(item => item.qualityIndex = ++item.qualityIndex);
videoList.unshift({
qualityIndex: 0,
width: 0,
height: 0,
bitrate: 0,
mediaType: 'video',
label: 'AUTO'
});
}

this.onGetBitrates.emit(videoList);
}
});

if (drmOptions) {
this.dash.setProtectionData(drmOptions);
}
Expand All @@ -87,6 +144,24 @@ export class VgDASH implements OnInit, OnChanges, OnDestroy {
}
}

setBitrate(bitrate: BitrateOption) {
if (this.dash) {
if (bitrate.qualityIndex > 0) {
if (this.dash.getAutoSwitchQualityFor(bitrate.mediaType)) {
this.dash.setAutoSwitchQualityFor(bitrate.mediaType, false);
}

if (this.vgDashAuto) {
this.dash.setQualityFor(bitrate.mediaType, bitrate.qualityIndex - 1);
} else {
this.dash.setQualityFor(bitrate.mediaType, bitrate.qualityIndex);
}
} else {
this.dash.setAutoSwitchQualityFor(bitrate.mediaType, true);
}
}
}

destroyPlayer() {
if (this.dash) {
this.dash.reset();
Expand Down

0 comments on commit 09a0f1f

Please sign in to comment.