Cleaning up the support list

This commit is contained in:
Zoe Roux 2020-10-22 23:58:36 +02:00
parent a988d2b75b
commit 199e541997
7 changed files with 232 additions and 400 deletions

View File

@ -22,7 +22,7 @@ import { PeopleListComponent } from './components/people-list/people-list.compon
import { import {
BufferToWidthPipe, BufferToWidthPipe,
FormatTimePipe, FormatTimePipe,
PlayerComponent, PlayerComponent, SupportedButtonPipe,
VolumeToButtonPipe VolumeToButtonPipe
} from "./pages/player/player.component"; } from "./pages/player/player.component";
import { SearchComponent } from './pages/search/search.component'; import { SearchComponent } from './pages/search/search.component';
@ -67,7 +67,8 @@ import { MatBadgeModule } from "@angular/material/badge";
ShowGridComponent, ShowGridComponent,
FormatTimePipe, FormatTimePipe,
BufferToWidthPipe, BufferToWidthPipe,
VolumeToButtonPipe VolumeToButtonPipe,
SupportedButtonPipe
], ],
imports: [ imports: [
BrowserModule, BrowserModule,

View File

@ -0,0 +1,155 @@
import { detect } from "detect-browser";
import { Track, WatchItem } from "../../models/watch-item";
export enum method
{
direct = "Direct Play",
transmux = "Transmux",
transcode = "Transcode"
}
export class SupportList
{
container: boolean;
videoCodec: boolean;
audioCodec: boolean[];
getPlaybackMethod(): method
{
if (this.container)
{
if (this.videoCodec && this.audioCodec)
return method.direct;
return method.transcode;
}
if (this.videoCodec && this.audioCodec)
return method.transmux;
return method.transcode;
}
}
export function getWhatIsSupported(player: HTMLVideoElement, item: WatchItem): SupportList
{
let supportList: SupportList = new SupportList();
let browser = detect();
if (!browser)
{
supportList.container = false;
supportList.videoCodec = false;
supportList.audioCodec = item.audios.map(() => false);
}
else
{
supportList.container = containerIsSupported(player, item.container, browser.name) && item.audios.length <= 1;
supportList.videoCodec = videoCodecIsSupported(player, item.video.codec, browser.name);
supportList.audioCodec = item.audios.map((x: Track) => audioCodecIsSupported(player, x.codec, browser.name));
}
return (supportList);
}
function containerIsSupported(player: HTMLVideoElement, container: string, browser: string): boolean
{
switch (container)
{
case "asf":
return browser == "tizen" || browser == "orsay" || browser == "edge";
case "avi":
return browser == "tizen" || browser == "orsay" || browser == "edge";
case "mpg":
case "mpeg":
return browser == "tizen" || browser == "orsay" || browser == "edge";
case "flv":
return browser == "tizen" || browser == "orsay";
case "3gp":
case "mts":
case "trp":
case "vob":
case "vro":
return browser == "tizen" || browser == "orsay";
case "mov":
return browser == "tizen" || browser == "orsay" || browser == "edge" || browser == "chrome";
case "m2ts":
return browser == "tizen" || browser == "orsay" || browser == "edge";
case "wmv":
return browser == "tizen" || browser == "orsay" || browser == "edge";
case "ts":
return browser == "tizen" || browser == "orsay" || browser == "edge";
case "mp4":
case "m4v":
return true;
case "mkv":
if (browser == "tizen" || browser == "orsay" || browser == "chrome" || browser == "edge")
return true;
return !!(player.canPlayType("video/x-matroska") || player.canPlayType("video/mkv"));
default:
return false;
}
}
//SHOULD CHECK FOR DEPTH (8bits ok but 10bits unsupported for almost every browsers)
function videoCodecIsSupported(player: HTMLVideoElement, codec: string, browser: string): boolean
{
switch (codec)
{
case "h264":
return !!player.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"');
case "h265":
case "hevc":
if (browser == "tizen" || browser == "orsay" || browser == "xboxOne" || browser == "ios")
return true;
//SHOULD SUPPORT CHROMECAST ULTRA
// if (browser.chromecast)
// {
// var isChromecastUltra = userAgent.indexOf('aarch64') !== -1;
// if (isChromecastUltra)
// {
// return true;
// }
// }
return !!player.canPlayType('video/hevc; codecs="hevc, aac"');
case "mpeg2video":
return browser == "orsay" || browser == "tizen" || browser == "edge";
case "vc1":
return browser == "orsay" || browser == "tizen" || browser == "edge";
case "msmpeg4v2":
return browser == "orsay" || browser == "tizen";
case "vp8":
return !!player.canPlayType('video/webm; codecs="vp8');
case "vp9":
return !!player.canPlayType('video/webm; codecs="vp9"');
case "vorbis":
return browser == "orsay" || browser == "tizen" || !!player.canPlayType('video/webm; codecs="vp8');
default:
return false;
}
}
//SHOULD CHECK FOR NUMBER OF AUDIO CHANNEL (2 ok but 5 not in some browsers)
function audioCodecIsSupported(player: HTMLVideoElement, codec: string, browser: string): boolean
{
switch (codec)
{
case "mp3":
return !!player.canPlayType('video/mp4; codecs="avc1.640029, mp4a.69"') ||
!!player.canPlayType('video/mp4; codecs="avc1.640029, mp4a.6B"');
case "aac":
return !!player.canPlayType('video/mp4; codecs="avc1.640029, mp4a.40.2"');
case "mp2":
return browser == "orsay" || browser == "tizen" || browser == "edge";
case "pcm_s16le":
case "pcm_s24le":
return browser == "orsay" || browser == "tizen" || browser == "edge";
case "aac_latm":
return browser == "orsay" || browser == "tizen";
case "opus":
return !!player.canPlayType('audio/ogg; codecs="opus"');
case "flac":
return browser == "orsay" || browser == "tizen" || browser == "edge";
default:
return false;
}
}

View File

@ -37,25 +37,25 @@
Video Container: Video Container:
<span> <span>
{{this.item.container}} {{this.item.container}}
<i class="material-icons">{{getSupportedFeature("container")}}</i> <i class="material-icons">{{this.supportList | supportedButton: "container"}}</i>
</span> </span>
<br /> <br />
Video Codec: Video Codec:
<span> <span>
{{this.item.video.codec}} {{this.item.video.codec}}
<i class="material-icons">{{getSupportedFeature("video")}}</i> <i class="material-icons">{{this.supportList | supportedButton: "video"}}</i>
</span> </span>
<br /> <br />
Audio Codec: Audio Codec:
<span> <span>
{{this.item.audios[0].codec}} {{this.item.audios[this.selectedAudio].codec}}
<i class="material-icons">{{getSupportedFeature("audio")}}</i> <i class="material-icons">{{this.supportList | supportedButton: "audio":this.selectedAudio}}</i>
</span> </span>
<br /> <br />
Subtitle Codec: Subtitle Codec:
<span> <span>
{{this.selectedSubtitle ? this.selectedSubtitle.codec : "none"}} {{this.selectedSubtitle != -1 ? this.item.subtitles[this.selectedSubtitle].codec : "none"}}
<i class="material-icons">{{getSupportedFeature("subtitle")}}</i> <i class="material-icons">{{this.supportList | supportedButton: "subtitle"}}</i>
</span> </span>
<br /> <br />
</mat-card-content> </mat-card-content>
@ -130,7 +130,8 @@
<p>{{player.currentTime | formatTime: player.duration}} / {{player.duration | formatTime}}</p> <p>{{player.currentTime | formatTime: player.duration}} / {{player.duration | formatTime}}</p>
</div> </div>
<div class="right"> <div class="right">
<button id="volume" *ngIf="this.item.audios.length > 1" mat-icon-button matTooltipPosition="above" matTooltip="Select audio track"> <button *ngIf="this.item.audios.length > 1" mat-icon-button
matTooltipPosition="above" matTooltip="Select audio track">
<mat-icon>music_note</mat-icon> <mat-icon>music_note</mat-icon>
</button> </button>
<button *ngIf="this.item.subtitles.length > 0" mat-icon-button [matMenuTriggerFor]="subtitles" <button *ngIf="this.item.subtitles.length > 0" mat-icon-button [matMenuTriggerFor]="subtitles"
@ -158,12 +159,13 @@
<mat-menu #subtitles="matMenu" (closed)="this.isMenuOpen = false"> <mat-menu #subtitles="matMenu" (closed)="this.isMenuOpen = false">
<ng-template matMenuContent> <ng-template matMenuContent>
<button [ngClass]="{'selected': this.selectedSubtitle == null}" mat-menu-item (click)="selectSubtitle(null)"> <button [ngClass]="{'selected': this.selectedSubtitle === -1}" mat-menu-item
(click)="selectSubtitle(null)">
<span>None</span> <span>None</span>
</button> </button>
<div *ngFor="let subtitle of this.item.subtitles"> <div *ngFor="let subtitle of this.item.subtitles; index as i">
<button [ngClass]="{'selected': this.selectedSubtitle == subtitle}" <button [ngClass]="{'selected': this.selectedSubtitle === i}"
mat-menu-item *ngIf="subtitle.codec === 'ass' || subtitle.codec === 'subrip'; mat-menu-item *ngIf="subtitle.codec === 'ass' || subtitle.codec === 'subrip';
else elseBlock" else elseBlock"
(click)="selectSubtitle(subtitle)"> (click)="selectSubtitle(subtitle)">

View File

@ -16,11 +16,10 @@ import { ActivatedRoute, Event, NavigationCancel, NavigationEnd, NavigationStart
import { OidcSecurityService } from "angular-auth-oidc-client"; import { OidcSecurityService } from "angular-auth-oidc-client";
import * as Hls from "hls.js"; import * as Hls from "hls.js";
import { import {
getPlaybackMethod,
getWhatIsSupported, getWhatIsSupported,
method, method,
SupportList SupportList
} from "../../../videoSupport/playbackMethodDetector"; } from "./playbackMethodDetector";
import { AppComponent } from "../../app.component"; import { AppComponent } from "../../app.component";
import { Track, WatchItem } from "../../models/watch-item"; import { Track, WatchItem } from "../../models/watch-item";
@ -76,6 +75,30 @@ export class VolumeToButtonPipe implements PipeTransform
} }
} }
@Pipe({
name: "supportedButton",
pure: true
})
export class SupportedButtonPipe implements PipeTransform
{
transform(supports: SupportList, selector: string, audioIndex: number = 0): string
{
if (!supports)
return "help";
switch (selector)
{
case "container":
return supports.container ? "check_circle" : "cancel";
case "video":
return supports.videoCodec ? "check_circle" : "cancel";
case "audio":
return supports.audioCodec[audioIndex] ? "check_circle" : "cancel";
default:
return "help";
}
}
}
@Component({ @Component({
selector: "app-player", selector: "app-player",
templateUrl: "./player.component.html", templateUrl: "./player.component.html",
@ -85,7 +108,8 @@ export class VolumeToButtonPipe implements PipeTransform
export class PlayerComponent implements OnInit, OnDestroy, AfterViewInit export class PlayerComponent implements OnInit, OnDestroy, AfterViewInit
{ {
item: WatchItem; item: WatchItem;
selectedSubtitle: Track; selectedAudio: number = 0;
selectedSubtitle: number = -1;
playMethod: method = method.direct; playMethod: method = method.direct;
supportList: SupportList; supportList: SupportList;
playing: boolean = true; playing: boolean = true;
@ -182,11 +206,11 @@ export class PlayerComponent implements OnInit, OnDestroy, AfterViewInit
switch (true) switch (true)
{ {
case event instanceof NavigationStart: case event instanceof NavigationStart:
this.loading = false; this.loading = true;
break; break;
case event instanceof NavigationEnd: case event instanceof NavigationEnd:
case event instanceof NavigationCancel: case event instanceof NavigationCancel:
this.loading = true; this.loading = false;
break; break;
default: default:
break; break;
@ -207,11 +231,25 @@ export class PlayerComponent implements OnInit, OnDestroy, AfterViewInit
ngAfterViewInit() ngAfterViewInit()
{ {
if (this.oidcSecurity === undefined)
this.oidcSecurity = this.injector.get(OidcSecurityService);
this.hlsPlayer.config.xhrSetup = xhr =>
{
const token = this.oidcSecurity.getToken();
if (token)
xhr.setRequestHeader("Authorization", "Bearer " + token);
};
this.showControls = true;
setTimeout(() => this.route.data.subscribe(() => setTimeout(() => this.route.data.subscribe(() =>
{ {
// TODO remove the query param for the method (should be a session setting).
let queryMethod: string = this.route.snapshot.queryParams["method"]; let queryMethod: string = this.route.snapshot.queryParams["method"];
this.selectPlayMethod(queryMethod ? method[queryMethod] : getPlaybackMethod(this.player, this.item)); this.supportList = getWhatIsSupported(this.player, this.item);
this.selectPlayMethod(queryMethod ? method[queryMethod] : this.supportList.getPlaybackMethod());
// TODO remove this, it should be a user's setting.
const subSlug: string = this.route.snapshot.queryParams["sub"]; const subSlug: string = this.route.snapshot.queryParams["sub"];
if (subSlug != null) if (subSlug != null)
{ {
@ -220,10 +258,7 @@ export class PlayerComponent implements OnInit, OnDestroy, AfterViewInit
const sub: Track = this.item.subtitles.find(x => x.language == languageCode && x.isForced == forced); const sub: Track = this.item.subtitles.find(x => x.language == languageCode && x.isForced == forced);
this.selectSubtitle(sub, false); this.selectSubtitle(sub, false);
} }
this.supportList = getWhatIsSupported(this.player, this.item);
})); }));
this.showControls = true;
} }
get isFullScreen(): boolean get isFullScreen(): boolean
@ -304,16 +339,6 @@ export class PlayerComponent implements OnInit, OnDestroy, AfterViewInit
selectPlayMethod(playMethod: method) selectPlayMethod(playMethod: method)
{ {
this.playMethod = playMethod; this.playMethod = playMethod;
if (this.oidcSecurity === undefined)
this.oidcSecurity = this.injector.get(OidcSecurityService);
this.hlsPlayer.config.xhrSetup = xhr =>
{
const token = this.oidcSecurity.getToken();
if (token)
xhr.setRequestHeader("Authorization", "Bearer " + token);
};
if (this.playMethod == method.direct) if (this.playMethod == method.direct)
this.player.src = `/video/${this.item.slug}`; this.player.src = `/video/${this.item.slug}`;
else else
@ -377,8 +402,16 @@ export class PlayerComponent implements OnInit, OnDestroy, AfterViewInit
document.body.requestFullscreen(); document.body.requestFullscreen();
} }
selectSubtitle(subtitle: Track, changeUrl: boolean = true) selectSubtitle(subtitle: Track | number, changeUrl: boolean = true)
{ {
if (typeof(subtitle) === "number")
{
this.selectedSubtitle = subtitle;
subtitle = this.item.subtitles[subtitle];
}
else
this.selectedSubtitle = this.item.subtitles.indexOf(subtitle);
if (changeUrl) if (changeUrl)
{ {
let subSlug: string; let subSlug: string;
@ -393,11 +426,10 @@ export class PlayerComponent implements OnInit, OnDestroy, AfterViewInit
relativeTo: this.route, relativeTo: this.route,
queryParams: {sub: subSlug}, queryParams: {sub: subSlug},
replaceUrl: true, replaceUrl: true,
queryParamsHandling: "merge" queryParamsHandling: "merge",
}); });
} }
this.selectedSubtitle = subtitle;
if (subtitle == null) if (subtitle == null)
{ {
@ -442,23 +474,6 @@ export class PlayerComponent implements OnInit, OnDestroy, AfterViewInit
} }
} }
getSupportedFeature(feature: string) : string
{
if (!this.supportList)
return "help";
switch (feature)
{
case "container":
return this.supportList.container ? "check_circle" : "cancel";
case "video":
return this.supportList.videoCodec ? "check_circle" : "cancel";
case "audio":
return this.supportList.audioCodec ? "check_circle" : "cancel";
default:
return "help";
}
}
removeHtmlTrack() removeHtmlTrack()
{ {
let elements = this.player.getElementsByTagName("track"); let elements = this.player.getElementsByTagName("track");
@ -466,17 +481,14 @@ export class PlayerComponent implements OnInit, OnDestroy, AfterViewInit
elements.item(0).remove(); elements.item(0).remove();
} }
getThumb(url: string)
{
return this.sanitizer.bypassSecurityTrustStyle("url(" + url + ")");
}
@HostListener("document:keyup", ["$event"]) @HostListener("document:keyup", ["$event"])
keypress(event: KeyboardEvent): void keypress(event: KeyboardEvent): void
{ {
switch (event.key) switch (event.key)
{ {
case " ": case " ":
case "k":
case "K":
this.togglePlayback(); this.togglePlayback();
break; break;
@ -499,18 +511,17 @@ export class PlayerComponent implements OnInit, OnDestroy, AfterViewInit
}); });
break; break;
case "v":
case "V": case "V":
const subtitleIndex: number = this.item.subtitles.indexOf(this.selectedSubtitle); this.selectSubtitle((this.selectedSubtitle + 2) % (this.item.subtitles.length + 1) - 1);
const nextSub: Track = subtitleIndex + 1 <= this.item.subtitles.length
? this.item.subtitles[subtitleIndex + 1]
: this.item.subtitles[0];
this.selectSubtitle(nextSub);
break; break;
case "f":
case "F": case "F":
this.fullscreen(); this.fullscreen();
break; break;
case "m":
case "M": case "M":
this.muted = !this.muted; this.muted = !this.muted;
this.snackBar.open(this.player.muted ? "Sound muted." : "Sound unmuted", null, { this.snackBar.open(this.player.muted ? "Sound muted." : "Sound unmuted", null, {
@ -521,10 +532,12 @@ export class PlayerComponent implements OnInit, OnDestroy, AfterViewInit
}); });
break; break;
case "n":
case "N": case "N":
this.next(); this.next();
break; break;
case "p":
case "P": case "P":
this.previous(); this.previous();
break; break;

View File

@ -1,159 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var detect_browser_1 = require("detect-browser");
var method;
(function (method) {
method["direct"] = "Direct Play";
method["transmux"] = "Transmux";
method["transcode"] = "Transcode";
})(method = exports.method || (exports.method = {}));
;
var SupportList = /** @class */ (function () {
function SupportList() {
}
return SupportList;
}());
exports.SupportList = SupportList;
function getPlaybackMethod(player, item) {
var supportList = getWhatIsSupported(player, item);
if (supportList.container) {
if (supportList.videoCodec && supportList.audioCodec)
return method.direct;
return method.transcode;
}
if (supportList.videoCodec && supportList.audioCodec)
return method.transmux;
return method.transcode;
}
exports.getPlaybackMethod = getPlaybackMethod;
function getWhatIsSupported(player, item) {
var supportList = new SupportList();
var browser = detect_browser_1.detect();
if (!browser) {
supportList.container = false;
supportList.videoCodec = false;
supportList.audioCodec = false;
}
else {
supportList.container = containerIsSupported(player, item.container, browser.name) && item.audios.length <= 1;
supportList.videoCodec = videoCodecIsSupported(player, item.video.codec, browser.name);
supportList.audioCodec = audioCodecIsSupported(player, item.audios.map(function (value) { return value.codec; }), browser.name);
}
return (supportList);
}
exports.getWhatIsSupported = getWhatIsSupported;
function containerIsSupported(player, container, browser) {
var supported = false;
switch (container) {
case "asf":
supported = browser == "tizen" || browser == "orsay" || browser == "edge";
//videoAudioCodecs = [];
break;
case "avi":
supported = browser == "tizen" || browser == "orsay" || browser == "edge";
break;
case "mpg":
case "mpeg":
supported = browser == "tizen" || browser == "orsay" || browser == "edge";
break;
case "flv":
supported = browser == "tizen" || browser == "orsay";
break;
case "3gp":
case "mts":
case "trp":
case "vob":
case "vro":
supported = browser == "tizen" || browser == "orsay";
break;
case "mov":
supported = browser == "tizen" || browser == "orsay" || browser == "edge" || browser == "chrome";
break;
case "m2ts":
supported = browser == "tizen" || browser == "orsay" || browser == "edge";
break;
case "wmv":
supported = browser == "tizen" || browser == "orsay" || browser == "edge";
//videoAudioCodecs = [];
break;
case "ts":
supported = browser == "tizen" || browser == "orsay" || browser == "edge";
break;
case "mp4":
case "m4v":
supported = true;
break;
case "mkv":
supported = browser == "tizen" || browser == "orsay" || browser == "chrome" || browser == "edge";
if (supported)
break;
if (player.canPlayType("video/x-matroska") || player.canPlayType("video/mkv"))
supported = true;
break;
default:
break;
}
return supported;
}
//SHOULD CHECK FOR DEPTH (8bits ok but 10bits unsuported for almost every browsers)
function videoCodecIsSupported(player, codec, browser) {
switch (codec) {
case "h264":
return !!player.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"'); //The !! is used to parse the string as a bool
case "h265":
case "hevc":
if (browser == "tizen" || browser == "orsay" || browser == "xboxOne" || browser == "ios")
return true;
//SHOULD SUPPORT CHROMECAST ULTRA
// if (browser.chromecast)
// {
// var isChromecastUltra = userAgent.indexOf('aarch64') !== -1;
// if (isChromecastUltra)
// {
// return true;
// }
// }
return !!player.canPlayType('video/hevc; codecs="hevc, aac"');
case "mpeg2video":
return browser == "orsay" || browser == "tizen" || browser == "edge";
case "vc1":
return browser == "orsay" || browser == "tizen" || browser == "edge";
case "msmpeg4v2":
return browser == "orsay" || browser == "tizen";
case "vp8":
return !!player.canPlayType('video/webm; codecs="vp8');
case "vp9":
return !!player.canPlayType('video/webm; codecs="vp9"');
case "vorbis":
return browser == "orsay" || browser == "tizen" || !!player.canPlayType('video/webm; codecs="vp8');
default:
return false;
}
}
//SHOULD CHECK FOR NUMBER OF AUDIO CHANNEL (2 ok but 5 not in some browsers)
function audioCodecIsSupported(player, codecs, browser) {
for (var _i = 0, codecs_1 = codecs; _i < codecs_1.length; _i++) {
var codec = codecs_1[_i];
switch (codec) {
case "mp3":
return !!player.canPlayType('video/mp4; codecs="avc1.640029, mp4a.69"') ||
!!player.canPlayType('video/mp4; codecs="avc1.640029, mp4a.6B"');
case "aac":
return !!player.canPlayType('video/mp4; codecs="avc1.640029, mp4a.40.2"');
case "mp2":
return browser == "orsay" || browser == "tizen" || browser == "edge";
case "pcm_s16le":
case "pcm_s24le":
return browser == "orsay" || browser == "tizen" || browser == "edge";
case "aac_latm":
return browser == "orsay" || browser == "tizen";
case "opus":
return !!player.canPlayType('audio/ogg; codecs="opus"');
case "flac":
return browser == "orsay" || browser == "tizen" || browser == "edge";
default:
return false;
}
}
}
//# sourceMappingURL=playbackMethodDetector.js.map

View File

@ -1 +0,0 @@
{"version":3,"file":"playbackMethodDetector.js","sourceRoot":"","sources":["playbackMethodDetector.ts"],"names":[],"mappings":"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"}

View File

@ -1,179 +0,0 @@
import { detect } from "detect-browser";
import { Track, WatchItem } from "../app/models/watch-item";
export enum method
{
direct = "Direct Play",
transmux = "Transmux",
transcode = "Transcode"
}
export class SupportList
{
container: boolean;
videoCodec: boolean;
audioCodec: boolean;
}
export function getPlaybackMethod(player: HTMLVideoElement, item: WatchItem): method
{
let supportList: SupportList = getWhatIsSupported(player, item);
if (supportList.container)
{
if (supportList.videoCodec && supportList.audioCodec)
return method.direct;
return method.transcode;
}
if (supportList.videoCodec && supportList.audioCodec)
return method.transmux;
return method.transcode;
}
export function getWhatIsSupported(player: HTMLVideoElement, item: WatchItem): SupportList
{
let supportList: SupportList = new SupportList();
let browser = detect();
if (!browser)
{
supportList.container = false;
supportList.videoCodec = false;
supportList.audioCodec = false;
}
else
{
supportList.container = containerIsSupported(player, item.container, browser.name) && item.audios.length <= 1;
supportList.videoCodec = videoCodecIsSupported(player, item.video.codec, browser.name);
supportList.audioCodec = audioCodecIsSupported(player, item.audios.map((value: Track) => value.codec), browser.name);
}
return (supportList);
}
function containerIsSupported(player: HTMLVideoElement, container: string, browser: string): boolean
{
let supported: boolean = false;
switch (container)
{
case "asf":
supported = browser == "tizen" || browser == "orsay" || browser == "edge";
//videoAudioCodecs = [];
break;
case "avi":
supported = browser == "tizen" || browser == "orsay" || browser == "edge";
break;
case "mpg":
case "mpeg":
supported = browser == "tizen" || browser == "orsay" || browser == "edge";
break;
case "flv":
supported = browser == "tizen" || browser == "orsay";
break;
case "3gp":
case "mts":
case "trp":
case "vob":
case "vro":
supported = browser == "tizen" || browser == "orsay";
break;
case "mov":
supported = browser == "tizen" || browser == "orsay" || browser == "edge" || browser == "chrome";
break;
case "m2ts":
supported = browser == "tizen" || browser == "orsay" || browser == "edge";
break;
case "wmv":
supported = browser == "tizen" || browser == "orsay" || browser == "edge";
//videoAudioCodecs = [];
break;
case "ts":
supported = browser == "tizen" || browser == "orsay" || browser == "edge";
break;
case "mp4":
case "m4v":
supported = true;
break;
case "mkv":
supported = browser == "tizen" || browser == "orsay" || browser == "chrome" || browser == "edge";
if (supported)
break;
if (player.canPlayType("video/x-matroska") || player.canPlayType("video/mkv"))
supported = true;
break;
default:
break;
}
return supported;
}
//SHOULD CHECK FOR DEPTH (8bits ok but 10bits unsuported for almost every browsers)
function videoCodecIsSupported(player: HTMLVideoElement, codec: string, browser: string): boolean
{
switch (codec)
{
case "h264":
return !!player.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"'); //The !! is used to parse the string as a bool
case "h265":
case "hevc":
if (browser == "tizen" || browser == "orsay" || browser == "xboxOne" || browser == "ios")
return true;
//SHOULD SUPPORT CHROMECAST ULTRA
// if (browser.chromecast)
// {
// var isChromecastUltra = userAgent.indexOf('aarch64') !== -1;
// if (isChromecastUltra)
// {
// return true;
// }
// }
return !!player.canPlayType('video/hevc; codecs="hevc, aac"');
case "mpeg2video":
return browser == "orsay" || browser == "tizen" || browser == "edge";
case "vc1":
return browser == "orsay" || browser == "tizen" || browser == "edge";
case "msmpeg4v2":
return browser == "orsay" || browser == "tizen";
case "vp8":
return !!player.canPlayType('video/webm; codecs="vp8');
case "vp9":
return !!player.canPlayType('video/webm; codecs="vp9"');
case "vorbis":
return browser == "orsay" || browser == "tizen" || !!player.canPlayType('video/webm; codecs="vp8');
default:
return false;
}
}
//SHOULD CHECK FOR NUMBER OF AUDIO CHANNEL (2 ok but 5 not in some browsers)
function audioCodecIsSupported(player: HTMLVideoElement, codecs: string[], browser: string): boolean
{
for (let codec of codecs)
{
switch (codec)
{
case "mp3":
return !!player.canPlayType('video/mp4; codecs="avc1.640029, mp4a.69"') ||
!!player.canPlayType('video/mp4; codecs="avc1.640029, mp4a.6B"');
case "aac":
return !!player.canPlayType('video/mp4; codecs="avc1.640029, mp4a.40.2"');
case "mp2":
return browser == "orsay" || browser == "tizen" || browser == "edge";
case "pcm_s16le":
case "pcm_s24le":
return browser == "orsay" || browser == "tizen" || browser == "edge";
case "aac_latm":
return browser == "orsay" || browser == "tizen";
case "opus":
return !!player.canPlayType('audio/ogg; codecs="opus"');
case "flac":
return browser == "orsay" || browser == "tizen" || browser == "edge";
default:
return false;
}
}
}