Merge branch 'dev' into av_filter

This commit is contained in:
Ingo Oppermann 2022-07-14 17:30:10 +02:00
commit 827f5bec54
No known key found for this signature in database
GPG Key ID: 2AB32426E9DD229E
16 changed files with 4650 additions and 15 deletions

View File

@ -6,6 +6,8 @@
- Add video h/v flip filter
- Add audio volume filter ([#313](https://github.com/datarhei/restreamer/issues/313))
- Add audio loudness normalization filter
- Add AirPlay support with silvermine videojs plugin
- Add Chromecast support (thx badincite, [#10](https://github.com/datarhei/restreamer-ui/pull/10))
- Add stream distribution across multiple internal servers
- Add SRT settings
- Add HLS version selection (Dwaynarang, Electra Player compatibility)

View File

@ -0,0 +1,15 @@
<svg fill="#FFFFFF" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<path d="M0 0h24v24H0V0z" id="a"/>
</defs>
<defs>
<path d="M0 0h24v24H0V0z" id="c"/>
</defs>
<clipPath id="b">
<use overflow="visible" xlink:href="#a"/>
</clipPath>
<clipPath clip-path="url(#b)" id="d">
<use overflow="visible" xlink:href="#c"/>
</clipPath>
<path clip-path="url(#d)" d="M6 22h12l-6-6zM21 3H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h4v-2H3V5h18v12h-4v2h4c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z"/>
</svg>

After

Width:  |  Height:  |  Size: 623 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 981 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 824 B

View File

@ -0,0 +1 @@
.vjs-airplay-button .vjs-icon-placeholder{background:url("ic_airplay_white_24px.svg") center center no-repeat;background-size:contain;display:inline-block;width:20px;height:20px}.vjs-airplay-button:hover{cursor:pointer}.vjs-airplay-button:hover .vjs-icon-placeholder{background-image:url("ic_airplay_white_24px.svg")}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,65 @@
/** Silvermine Chromecast **/
.vjs-chromecast-button .vjs-icon-placeholder {
background: url('ic_cast_white_24dp.png') center center no-repeat;
background-size: contain;
display: inline-block;
width: 20px;
height: 20px;
}
.vjs-chromecast-button:hover {
cursor: pointer;
}
.vjs-chromecast-button:hover .vjs-icon-placeholder {
background-image: url('ic_cast_white_24dp.png');
}
.vjs-chromecast-button.vjs-chromecast-casting-state .vjs-icon-placeholder {
background-image: url('ic_cast_connected_white_24dp.png');
}
.vjs-chromecast-button.vjs-chromecast-casting-state:hover .vjs-icon-placeholder {
background-image: url('ic_cast_connected_white_24dp.png');
}
.vjs-tech-chromecast {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
overflow: hidden;
}
.vjs-tech-chromecast .vjs-tech-chromecast-poster::after {
content: ' ';
display: block;
height: 2px;
width: 100px;
background-color: #cccccc;
position: absolute;
left: calc(50% - 50px);
}
.vjs-tech-chromecast .vjs-tech-chromecast-poster-img {
max-height: 180px;
width: auto;
border: 2px solid #cccccc;
}
.vjs-tech-chromecast .vjs-tech-chromecast-poster-img.vjs-tech-chromecast-poster-img-empty {
width: 160px;
height: 90px;
}
.vjs-tech-chromecast .vjs-tech-chromecast-title-container {
position: absolute;
bottom: 50%;
margin-bottom: 100px;
color: #cccccc;
text-align: center;
}
.vjs-tech-chromecast .vjs-tech-chromecast-title {
font-size: 22px;
}
.vjs-tech-chromecast .vjs-tech-chromecast-title.vjs-tech-chromecast-title-empty {
display: none;
}
.vjs-tech-chromecast .vjs-tech-chromecast-subtitle {
font-size: 18px;
padding-top: 0.5em;
}
.vjs-tech-chromecast .vjs-tech-chromecast-subtitle.vjs-tech-chromecast-subtitle-empty {
display: none;
}

View File

@ -0,0 +1 @@
.vjs-chromecast-button .vjs-icon-placeholder{background:url(ic_cast_white_24dp.png) center center no-repeat;background-size:contain;display:inline-block;width:20px;height:20px}.vjs-chromecast-button:hover{cursor:pointer}.vjs-chromecast-button:hover .vjs-icon-placeholder{background-image:url(ic_cast_white_24dp.png)}.vjs-chromecast-button.vjs-chromecast-casting-state .vjs-icon-placeholder{background-image:url(ic_cast_connected_white_24dp.png)}.vjs-chromecast-button.vjs-chromecast-casting-state:hover .vjs-icon-placeholder{background-image:url(ic_cast_connected_white_24dp.png)}.vjs-tech-chromecast{display:flex;flex-direction:column;justify-content:center;align-items:center;overflow:hidden}.vjs-tech-chromecast .vjs-tech-chromecast-poster::after{content:' ';display:block;height:2px;width:100px;background-color:#ccc;position:absolute;left:calc(50% - 50px)}.vjs-tech-chromecast .vjs-tech-chromecast-poster-img{max-height:180px;width:auto;border:2px solid #ccc}.vjs-tech-chromecast .vjs-tech-chromecast-poster-img.vjs-tech-chromecast-poster-img-empty{width:160px;height:90px}.vjs-tech-chromecast .vjs-tech-chromecast-title-container{position:absolute;bottom:50%;margin-bottom:100px;color:#ccc;text-align:center}.vjs-tech-chromecast .vjs-tech-chromecast-title{font-size:22px}.vjs-tech-chromecast .vjs-tech-chromecast-title.vjs-tech-chromecast-title-empty{display:none}.vjs-tech-chromecast .vjs-tech-chromecast-subtitle{font-size:18px;padding-top:.5em}.vjs-tech-chromecast .vjs-tech-chromecast-subtitle.vjs-tech-chromecast-subtitle-empty{display:none}

File diff suppressed because it is too large Load Diff

View File

@ -5,3 +5,10 @@ dist/videojs-overlay.min.css
dist/video-js-skin.min.css
dist/videojs-license.min.js
dist/videojs-license.min.css
dist/videojs-chromecast.min.js
dist/videojs-chromecast.min.css
dist/ic_cast_connected_white_24dp.png
dist/ic_cast_white_24dp.png
dist/videojs-airplay.min.js
dist/videojs-airplay.min.css
dist/ic_airplay_white_24px.svg

View File

@ -15,11 +15,12 @@
<link href="player/videojs/dist/video-js-skin.min.css" rel="stylesheet">
<link href="player/videojs/dist/videojs-overlay.min.css" rel="stylesheet">
<link href="player/videojs/dist/videojs-license.min.css" rel="stylesheet">
<style>
.player-poster[data-poster] .poster-background[data-poster] {
height: initial !important;
}
</style>
{{#if airplay}}
<link href="player/videojs/dist/videojs-airplay.min.css" rel="stylesheet">
{{/if}}
{{#if chromecast}}
<link href="player/videojs/dist/videojs-chromecast.min.css" rel="stylesheet">
{{/if}}
</head>
<body>
<div style="position:absolute;top:0;right:0;bottom:0;left:0">
@ -28,6 +29,13 @@
<script src="player/videojs/dist/video.min.js"></script>
<script src="player/videojs/dist/videojs-overlay.min.js"></script>
<script src="player/videojs/dist/videojs-license.min.js"></script>
{{#if airplay}}
<script src='player/videojs/dist/videojs-airplay.min.js'></script>
{{/if}}
{{#if chromecast}}
<script src='player/videojs/dist/videojs-chromecast.min.js'></script>
<script src='https://www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1'></script>
{{/if}}
<script>
function getQueryParam(key, defaultValue) {
var query = window.location.search.substring(1);
@ -80,6 +88,15 @@
}
};
if (playerConfig.chromecast) {
config.techOrder = ["chromecast", "html5"];
config.plugins.chromecast = {};
}
if (playerConfig.airplay) {
config.plugins.airPlay = {};
}
var player = videojs('player', config);
player.ready(function() {
if(playerConfig.logo.image.length != 0) {

View File

@ -44,6 +44,12 @@
<link href="player/videojs/dist/video-js-skin.min.css" rel="stylesheet">
<link href="player/videojs/dist/videojs-overlay.min.css" rel="stylesheet">
<link href="player/videojs/dist/videojs-license.min.css" rel="stylesheet">
{{#if airplay}}
<link href="player/videojs/dist/videojs-airplay.min.css" rel="stylesheet">
{{/if}}
{{#if chromecast}}
<link href="player/videojs/dist/videojs-chromecast.min.css" rel="stylesheet">
{{/if}}
{{/ifEquals}}
<style>
/** flexboxgrid 6.3.1 **/
@ -782,6 +788,13 @@
<script src="player/videojs/dist/video.min.js"></script>
<script src="player/videojs/dist/videojs-overlay.min.js"></script>
<script src="player/videojs/dist/videojs-license.min.js"></script>
{{#if airplay}}
<script src='player/videojs/dist/videojs-airplay.min.js'></script>
{{/if}}
{{#if chromecast}}
<script src='player/videojs/dist/videojs-chromecast.min.js'></script>
<script src='https://www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1'></script>
{{/if}}
{{else}}
<script src="player/clappr/clappr.min.js"></script>
<script src="player/clappr/clappr-stats.min.js"></script>
@ -824,6 +837,8 @@
var mute = convertBoolParam("mute", playerConfig.mute);
var statistics = convertBoolParam("stats", playerConfig.statistics);
var color = convertColorParam("color", playerConfig.color.buttons);
var chromecast = {{#if chromecast}}true{{else}}false{{/if}};
var airplay = {{#if airplay}}true{{else}}false{{/if}};
</script>
<script src="playersite/player.js"></script>

View File

@ -2,17 +2,28 @@ var config = {
controls: true,
poster: playerConfig.poster + '?t=' + String(new Date().getTime()),
autoplay: autoplay ? 'muted' : false,
muted: mute,
muted: true,
liveui: true,
responsive: true,
fluid: true,
sources: [{ src: playerConfig.source, type: 'application/x-mpegURL' }],
// Needed to append the url orgin in order for the source to properly pass to the cast device
sources: [{ src: window.location.origin + '/' + playerConfig.source, type: 'application/x-mpegURL' }],
plugins: {
license: playerConfig.license,
},
};
if (chromecast) {
config.techOrder = ['chromecast', 'html5'];
config.plugins.chromecast = {};
}
if (airplay) {
config.plugins.airPlay = {};
}
var player = videojs('player', config);
player.ready(function () {
if (playerConfig.logo.image.length != 0) {
var overlay = null;

View File

@ -358,7 +358,9 @@ class Restreamer {
}
compatibility.core.have = this.Version().number;
compatibility.ffmpeg.have = this.skills.ffmpeg.version;
if (this.skills?.ffmpeg?.version) {
compatibility.ffmpeg.have = this.skills.ffmpeg.version;
}
compatibility.core.compatible = SemverSatisfies(compatibility.core.have, compatibility.core.want);
compatibility.ffmpeg.compatible = SemverSatisfies(compatibility.ffmpeg.have, compatibility.ffmpeg.want);
@ -371,8 +373,13 @@ class Restreamer {
}
async _init() {
await this._initConfig();
const compatibility = this.Compatibility();
if (!compatibility.compatible) {
return;
}
await this._initSkills();
await this._initConfig();
await this._discoverChannels();
}
@ -895,6 +902,10 @@ class Restreamer {
}
ConfigOverrides(name) {
if (!this.config) {
return false;
}
return this.config.overrides.includes(name);
}
@ -1889,10 +1900,16 @@ class Restreamer {
// Set defaults for the settings of the selfhosted player
InitPlayerSettings(initSettings) {
if (!initSettings) {
initSettings = {};
}
const settings = {
autoplay: false,
mute: false,
statistics: false,
chromecast: false,
airplay: false,
color: {},
ga: {},
logo: {},
@ -1936,6 +1953,8 @@ class Restreamer {
return false;
}
metadata.player = this.InitPlayerSettings(metadata.player);
const templateData = {
channelid: channelid,
name: metadata.meta.name,
@ -1948,6 +1967,8 @@ class Restreamer {
poster_url: this.GetIngestPosterUrlAddresses(channelid)[0],
width: 640,
height: 360,
chromecast: metadata.player.chromecast,
airplay: metadata.player.airplay,
};
// upload player.html
@ -1981,10 +2002,6 @@ class Restreamer {
}
async UpdatePlayerConfig(channelid, metadata) {
if (!('player' in metadata)) {
metadata.player = {};
}
metadata.player = this.InitPlayerSettings(metadata.player);
const playerConfig = {
@ -2033,6 +2050,8 @@ class Restreamer {
title: 'restreamer',
share: true,
support: true,
chromecast: false,
airplay: false,
template: '!default',
templatename: '',
textcolor_title: 'rgba(255,255,255,1)',
@ -2114,6 +2133,8 @@ class Restreamer {
title: settings.title,
share: settings.share,
support: settings.support,
chromecast: settings.chromecast,
airplay: settings.airplay,
url: this.GetPlayersiteUrl(),
textcolor_title: settings.textcolor_title,
textcolor_default: settings.textcolor_default,

View File

@ -98,7 +98,7 @@ export default function Playersite(props) {
const value = event.target.value;
const settings = $settings;
if (['playersite', 'header', 'share', 'support'].includes(what)) {
if (['playersite', 'header', 'share', 'support', 'chromecast', 'airplay'].includes(what)) {
settings[what] = !settings[what];
} else {
settings[what] = value;
@ -420,6 +420,22 @@ export default function Playersite(props) {
onChange={handleChange('share')}
/>
</Grid>
<Grid item xs={12}>
<Checkbox
label={<Trans>Chromecast</Trans>}
checked={$settings.chromecast}
disabled={!$settings.playersite}
onChange={handleChange('chromecast')}
/>
</Grid>
<Grid item xs={12}>
<Checkbox
label={<Trans>AirPlay</Trans>}
checked={$settings.airplay}
disabled={!$settings.playersite}
onChange={handleChange('airplay')}
/>
</Grid>
<Grid item xs={12}>
<Checkbox
label={<Trans>Support datarhei Restreamer</Trans>}

View File

@ -118,7 +118,7 @@ export default function Edit(props) {
const settings = $settings;
if (section === '') {
if (['autoplay', 'mute', 'statistics'].includes(what)) {
if (['autoplay', 'mute', 'statistics', 'chromecast', 'airplay'].includes(what)) {
settings[what] = !settings[what];
} else {
settings[what] = value;
@ -436,6 +436,8 @@ export default function Edit(props) {
<Grid item xs={12}>
<Checkbox label={<Trans>Autoplay</Trans>} checked={$settings.autoplay} onChange={handleChange('autoplay')} />
<Checkbox label={<Trans>Mute</Trans>} checked={$settings.mute} onChange={handleChange('mute')} />
<Checkbox label={<Trans>Chromecast</Trans>} checked={$settings.chromecast} onChange={handleChange('chromecast')} />
<Checkbox label={<Trans>AirPlay</Trans>} checked={$settings.airplay} onChange={handleChange('airplay')} />
</Grid>
</Grid>
</TabPanel>