Significant video player improvements
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
<div class="block block-{{ include.type }}
|
||||
<div {% if include.id %}id="{{include.id}}"{% endif %} class="block block-{{ include.type }}
|
||||
{% if include.float == "clear" %}float-clear{% elsif include.float %}float float-{{ include.float }}{% endif %}
|
||||
{% if include.align %}align-{{ include.align }}{% endif %}" style="
|
||||
{% if include.align %}align-{{ include.align }}{% endif %}
|
||||
{% if include.class %}{{ include.class }}{% endif %}" style="
|
||||
{% if include.width %}width: {{ include.width }};{% endif %}">
|
||||
{{ include.content }}
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,21 @@
|
||||
{% capture content %}
|
||||
{% if include.player %}
|
||||
<div class="top">
|
||||
<select class="variant"></select>
|
||||
</div>
|
||||
<div class="bottom">
|
||||
<span class="play"></span>
|
||||
<div class="playback">
|
||||
<span class="time">0:00 / 0:00</span>
|
||||
<input type="range" class="progress">
|
||||
</div>
|
||||
<div class="audio">
|
||||
<span class="mute"></span>
|
||||
<input type="range" class="volume">
|
||||
</div>
|
||||
<span class="fullscreen"></span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if include.link %}<a href="{% if include.link == true %}{{ include.url }}{% else %}{{ include.link }}{% endif %}" {% if include.caption %}alt="{{ include.caption }}"{% endif %} target="_blank">{% endif %}
|
||||
{% assign file_info = site.static_files | where: "path", include.url %}
|
||||
{% if include.type == "image" %}
|
||||
@@ -18,7 +35,7 @@
|
||||
{% if include.lazyload %}</noscript>{% endif %}
|
||||
</picture>
|
||||
{% elsif include.type == "video" %}
|
||||
<video controls
|
||||
<video {% if include.player %}{% else %}controls{% endif %}
|
||||
class="content"
|
||||
{% if include.lazyload %}data-lazyload=""{% endif %}
|
||||
{% if include.poster %}poster="{{ include.poster | absolute_url }}"{% endif %}
|
||||
@@ -59,4 +76,10 @@
|
||||
{% if include.caption %}
|
||||
<figcaption>{{ include.caption }}</figcaption>
|
||||
{% endif %}
|
||||
{% endcapture %}{% include blocks/_base.liquid type="media" content=content float=include.float align=include.align %}
|
||||
{% endcapture %}
|
||||
{% if include.player %}
|
||||
{% assign class = include.type | append: " player" %}
|
||||
{% else %}
|
||||
{% assign class = include.type %}
|
||||
{% endif %}
|
||||
{% include blocks/_base.liquid type="media" class=class content=content float=include.float align=include.align id=include.id %}
|
||||
@@ -52,65 +52,94 @@ videos:
|
||||
# url: "https://cdn.xaymar.com/blog/2022/01/hes/nvidia-ampere/:video-:res-:ratek-nv265.mp4"
|
||||
---
|
||||
|
||||
{% capture content %}Almost three years have passed since I did my original full hardware encoder showdown, and the field has changed drastically since then. NVIDIA brought out the Turing and Ampere architecture, AMD finally designed a useful GPU architecture, and even Intel is entering the market now. Finally no longer a Red vs Green battle, but a Red vs Green vs Blue - the full RGB experience! But also due to that, my data is now well beyond outdated, and its time to refresh.{% endcapture %}{% include blocks/paragraph.liquid content=content %}
|
||||
{% capture content %}Almost three years have passed since I did my original full hardware encoder showdown, and the field has changed drastically since then. NVIDIA brought out the Turing and Ampere architecture, AMD finally designed a useful GPU architecture, and even Intel is entering the market now. Finally no longer a Red vs Green battle, but a Red vs Green vs Blue - the full RGB experience! But also due to that, my data is now well beyond outdated, and its time to refresh.{% endcapture %}
|
||||
{% include blocks/paragraph.liquid content=content %}
|
||||
|
||||
{% capture content %}Unfortunately for me, sourcing hardware for testing purposes right now is near impossible at reasonable prices, so I had to opt for unusual methods to acquire it. Instead of buying a dedicated AMD GPU - which would have been very expensive - I instead opted for a Laptop with Dual-AMD Graphics and an AMD CPU. The latter to fulfill my daily usage needs (compiling software), the former to fulfill testing conditions for video encoding. In any case, it was two flies with one stone - or however that saying goes - and I had what I needed.{% endcapture %}{% include blocks/paragraph.liquid content=content %}
|
||||
{% capture content %}Unfortunately for me, sourcing hardware for testing purposes right now is near impossible at reasonable prices, so I had to opt for unusual methods to acquire it. Instead of buying a dedicated AMD GPU - which would have been very expensive - I instead opted for a Laptop with Dual-AMD Graphics and an AMD CPU. The latter to fulfill my daily usage needs (compiling software), the former to fulfill testing conditions for video encoding. In any case, it was two flies with one stone - or however that saying goes - and I had what I needed.{% endcapture %}
|
||||
{% include blocks/paragraph.liquid content=content %}
|
||||
|
||||
{% capture content %}So, as it is usual, I drank my coffee as my automated script did what I told it to do. Then I fixed the automated script to actually do what I thought it did, and repeated this charade twice more because it's obviously fun, and I definitely did not forget to put an "s" at the end of a variable reference - twice. Without any further interruptions, here are a few hundred video examples for your watching pleasure.{% endcapture %}{% include blocks/paragraph.liquid content=content %}
|
||||
{% capture content %}So, as it is usual, I drank my coffee as my automated script did what I told it to do. Then I fixed the automated script to actually do what I thought it did, and repeated this charade twice more because it's obviously fun, and I definitely did not forget to put an "s" at the end of a variable reference - twice. Without any further interruptions, here are a few hundred video examples for your watching pleasure.{% endcapture %}
|
||||
{% include blocks/paragraph.liquid content=content %}
|
||||
|
||||
{% capture content %}<b>Update 2022-01-11:</b> Added x264 veryfast files and newly discovered AMF settings that drastically improve quality.{% endcapture %}{% include blocks/paragraph.liquid content=content %}
|
||||
{% capture content %}
|
||||
<b>Update 2022-01-11:</b>
|
||||
Added x264 veryfast files and newly discovered AMF settings that drastically improve quality.{% endcapture %}
|
||||
{% include blocks/paragraph.liquid content=content %}
|
||||
|
||||
{% for video in page.videos.videos %}
|
||||
{% capture capture_video %}
|
||||
{% for bitrate in page.videos.bitrates %}
|
||||
{% assign title_bitrate=bitrate | append: "kbit" %}
|
||||
{% capture capture_bitrate %}
|
||||
{% for resolution in page.videos.resolutions %}
|
||||
{% capture capture_resolution %}
|
||||
{% capture columns %}
|
||||
{% include blocks/media.liquid type="video" caption=caption poster=video.poster muted=true preload="metadata" id=video.id player=true %}
|
||||
<script>
|
||||
document.getElementById("{{ video.id }}").variants = {
|
||||
{% for bitrate in page.videos.bitrates %}"{{ bitrate }}kbit/s" : {
|
||||
{% for resolution in page.videos.resolutions %}"{{ resolution }}" : {
|
||||
{% for encoder in page.videos.encoders %}
|
||||
{% capture column %}
|
||||
{% assign caption=encoder.name %}
|
||||
{% assign real_url=encoder.url | replace: ":video", video.id | replace: ":res", resolution | replace: ":rate", bitrate | absolute_url %}
|
||||
{% include blocks/media.liquid type="video" url=real_url caption=caption poster=video.poster muted=true preload="none" lazyload=true %}
|
||||
{% endcapture %}{% include blocks/column.liquid content=column %}
|
||||
{% endfor %}
|
||||
{% endcapture %}{% include blocks/columns.liquid content=columns %}
|
||||
{% endcapture %}{% include blocks/details.liquid title=resolution content=capture_resolution level=3 %}
|
||||
{% endfor %}
|
||||
{% endcapture %}{% include blocks/details.liquid title=title_bitrate content=capture_bitrate level=2 %}
|
||||
{% endfor %}
|
||||
{% endcapture %}{% include blocks/details.liquid title=video.name content=capture_video level=1 open=true %}
|
||||
{% assign real_url = encoder.url | replace: ":video", video.id | replace: ":res", resolution | replace: ":rate", bitrate | absolute_url%}"{{ encoder.name }}" : "{{ real_url }}",{% endfor %}
|
||||
},{% endfor %}
|
||||
},{% endfor %}
|
||||
};
|
||||
</script>
|
||||
{% endcapture %}
|
||||
{% include blocks/details.liquid title=video.name content=capture_video level=1 open=true %}
|
||||
{% endfor %}
|
||||
|
||||
{% include blocks/heading.liquid content="Results" level=1 %}
|
||||
{% capture content %}I'm glad I switched to a useful Software for making websites, otherwise adding this many videos would have taken more than a week to do. I've ran every video through VMAF, SSIM and PSNR - which took a long while, but that's why I have multiple computers - and then combined all the data. While SSIM was usually an outlier, overall it did not deviate much from PSNR or VMAF.{% endcapture %}{% include blocks/paragraph.liquid content=content %}
|
||||
{% capture content %}I'm glad I switched to a useful Software for making websites, otherwise adding this many videos would have taken more than a week to do. I've ran every video through VMAF, SSIM and PSNR - which took a long while, but that's why I have multiple computers - and then combined all the data. While SSIM was usually an outlier, overall it did not deviate much from PSNR or VMAF.{% endcapture %}
|
||||
{% include blocks/paragraph.liquid content=content %}
|
||||
|
||||
{% capture content %}Unfortunately I no longer have all the hardware available, so some of the results are based on extrapolated data - which might paint it in a much nicer light than it really is, or a much worse light. Additionally, I have not uploaded a few files in order to save on space on the CDN machine. Space is expensive, and sadly I can't yet just grow money - no matter how much the Cryptocurrency people believe it is possible to do that. In any case, here is the placement for each H264 encoder, rated by PSNR, SSIM and VMAF: {% endcapture %}{% include blocks/paragraph.liquid content=content %}
|
||||
{% capture content %}Unfortunately I no longer have all the hardware available, so some of the results are based on extrapolated data - which might paint it in a much nicer light than it really is, or a much worse light. Additionally, I have not uploaded a few files in order to save on space on the CDN machine. Space is expensive, and sadly I can't yet just grow money - no matter how much the Cryptocurrency people believe it is possible to do that. In any case, here is the placement for each H264 encoder, rated by PSNR, SSIM and VMAF:
|
||||
{% endcapture %}
|
||||
{% include blocks/paragraph.liquid content=content %}
|
||||
|
||||
{% capture content %}
|
||||
<li>x264 placebo <sub>(Hidden)</sub><br>
|
||||
x264 veryslow <sub>(Hidden)</sub></li>
|
||||
<li>x264 slower <sub>(Hidden)</sub></li>
|
||||
<li>x264 slow <sub>(Hidden)</sub></li>
|
||||
<li>NVIDIA NVENC Turing/Ampere</li>
|
||||
<li>x264 medium</li>
|
||||
<li>x264 fast <sub>(Hidden)</sub><br>
|
||||
x264 faster <sub>(Hidden)</sub></li>
|
||||
<li>x264 veryfast<br>
|
||||
NVIDIA NVENC Pascal <sub>(Estimated from previous Test Run)</sub><br>
|
||||
<li>x264 placebo
|
||||
<sub>(Hidden)</sub><br>
|
||||
x264 veryslow
|
||||
<sub>(Hidden)</sub>
|
||||
</li>
|
||||
<li>x264 slower
|
||||
<sub>(Hidden)</sub>
|
||||
</li>
|
||||
<li>x264 slow
|
||||
<sub>(Hidden)</sub>
|
||||
</li>
|
||||
<li>NVIDIA NVENC Turing/Ampere</li>
|
||||
<li>x264 medium</li>
|
||||
<li>x264 fast
|
||||
<sub>(Hidden)</sub><br>
|
||||
x264 faster
|
||||
<sub>(Hidden)</sub>
|
||||
</li>
|
||||
<li>x264 veryfast<br>
|
||||
NVIDIA NVENC Pascal
|
||||
<sub>(Estimated from previous Test Run)</sub><br>
|
||||
AMD AMF RDNA2 (HQ)</li>
|
||||
<li>AMD AMF RDNA2 (HQ+MB)</li>
|
||||
<li>AMD AMF GCN3 and GCN3 <sub>(Estimated from previous Test Run)</sub></li>
|
||||
<li>x264 superfast <sub>(Hidden)</sub></li>
|
||||
<li>AMD AMF RDNA2</li>
|
||||
<li>AMD AMF GCN5 (Vega)</li>
|
||||
<li>AMD AMF GCN3.4 and GCN4 (Polaris) <sub>(Estimated from previous Test Run)</sub></li>
|
||||
<li>AMD AMF GCN2 <sub>(Estimated from previous Test Run)</sub></li>
|
||||
<li>AMD AMF GCN1 <sub>(Estimated from previous Test Run)</sub></li>
|
||||
<li>x264 ultrafast <sub>(Hidden)</sub></li>
|
||||
{% endcapture %}{% include blocks/list.liquid content=content ordered=true %}
|
||||
<li>AMD AMF RDNA2 (HQ+MB)</li>
|
||||
<li>AMD AMF GCN3 and GCN3
|
||||
<sub>(Estimated from previous Test Run)</sub>
|
||||
</li>
|
||||
<li>x264 superfast
|
||||
<sub>(Hidden)</sub>
|
||||
</li>
|
||||
<li>AMD AMF RDNA2</li>
|
||||
<li>AMD AMF GCN5 (Vega)</li>
|
||||
<li>AMD AMF GCN3.4 and GCN4 (Polaris)
|
||||
<sub>(Estimated from previous Test Run)</sub>
|
||||
</li>
|
||||
<li>AMD AMF GCN2
|
||||
<sub>(Estimated from previous Test Run)</sub>
|
||||
</li>
|
||||
<li>AMD AMF GCN1
|
||||
<sub>(Estimated from previous Test Run)</sub>
|
||||
</li>
|
||||
<li>x264 ultrafast
|
||||
<sub>(Hidden)</sub>
|
||||
</li>
|
||||
{% endcapture %}
|
||||
{% include blocks/list.liquid content=content ordered=true %}
|
||||
|
||||
{% capture content %}As expected, x264's presets are all a quality step higher than the next faster one - except for placebo, which had no room to shine here. The "new" NVIDIA Turing/Ampere NVENC also proves its place by winning just enough comparisons to sit between slow and medium. AMD also appears to finally have moved some heads around and improved their encoder. Perhaps over the next year, AMD, NVIDIA and Intel will invade the hardware encoder scene with new and exciting things.{% endcapture %}{% include blocks/paragraph.liquid content=content %}
|
||||
{% capture content %}As expected, x264's presets are all a quality step higher than the next faster one - except for placebo, which had no room to shine here. The "new" NVIDIA Turing/Ampere NVENC also proves its place by winning just enough comparisons to sit between slow and medium. AMD also appears to finally have moved some heads around and improved their encoder. Perhaps over the next year, AMD, NVIDIA and Intel will invade the hardware encoder scene with new and exciting things.{% endcapture %}
|
||||
{% include blocks/paragraph.liquid content=content %}
|
||||
|
||||
{% capture content %}Anyway, with my piece said, and tons of videos here, I will say goodbye and leave you with this wall of videos to scout through.{% endcapture %}{% include blocks/paragraph.liquid content=content %}
|
||||
{% capture content %}Anyway, with my piece said, and tons of videos here, I will say goodbye and leave you with this wall of videos to scout through.{% endcapture %}
|
||||
{% include blocks/paragraph.liquid content=content %}
|
||||
@@ -2,6 +2,8 @@
|
||||
// Block: Media
|
||||
// --------------------------------------------------------------------------------
|
||||
.block-media {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.block-media > a > .content,
|
||||
@@ -17,6 +19,155 @@
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
// Block: Media Player
|
||||
// --------------------------------------------------------------------------------
|
||||
|
||||
.block-media.hide {
|
||||
cursor: none;
|
||||
}
|
||||
|
||||
.block-media > .top,
|
||||
.block-media > .bottom {
|
||||
margin: 0;
|
||||
padding: 0.5rem .5rem;
|
||||
}
|
||||
|
||||
.block-media > .top {
|
||||
background: linear-gradient(to top, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.8) 80%);
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
display: grid;
|
||||
grid-template-columns: 1;
|
||||
grid-template-rows: 1;
|
||||
justify-content: stretch;
|
||||
align-items: stretch;
|
||||
gap: 0;
|
||||
transition: top ease-in-out 333ms, opacity ease-in-out 333ms;
|
||||
opacity: 1.0;
|
||||
}
|
||||
|
||||
.block-media.hide > .top {
|
||||
top: -100%;
|
||||
opacity: 0.0;
|
||||
}
|
||||
|
||||
.block-media > .top > .variant {
|
||||
// ToDo: Style this
|
||||
}
|
||||
|
||||
.block-media > .bottom {
|
||||
background: linear-gradient(to bottom, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.8) 80%);
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
display: grid;
|
||||
grid-template-columns: max-content 1fr max-content max-content;
|
||||
justify-content: stretch;
|
||||
align-items: stretch;
|
||||
gap: 0.5rem;
|
||||
overflow: hidden;
|
||||
user-select: none;
|
||||
transition: bottom ease-in-out 333ms, opacity ease-in-out 333ms;
|
||||
opacity: 1.0;
|
||||
}
|
||||
|
||||
.block-media.hide > .bottom {
|
||||
bottom: -100%;
|
||||
}
|
||||
|
||||
.block-media > .bottom > .play {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: 2rem;
|
||||
line-height: 2rem;
|
||||
text-align: center;
|
||||
user-select: none;
|
||||
}
|
||||
.block-media > .bottom > .play.playing {
|
||||
}
|
||||
|
||||
.block-media > .bottom > .playback {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: grid;
|
||||
grid-template-columns: max-content 1fr max-content;
|
||||
justify-content: stretch;
|
||||
align-items: stretch;
|
||||
gap: 0.5rem;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.block-media > .bottom > .playback > .time {
|
||||
display: block;
|
||||
height: 2rem;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
text-align: right;
|
||||
line-height: 2rem;
|
||||
user-select: auto;
|
||||
}
|
||||
|
||||
.block-media > .bottom > .playback > .progress {
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: auto;
|
||||
height: 100%;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.block-media > .bottom > .audio {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: grid;
|
||||
grid-template-columns: max-content 1fr;
|
||||
justify-content: stretch;
|
||||
align-items: stretch;
|
||||
gap: 0.5rem;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.block-media > .bottom > .audio > .mute {
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
font-size: 2rem;
|
||||
line-height: 2rem;
|
||||
text-align: center;
|
||||
user-select: none;
|
||||
}
|
||||
.block-media > .bottom > .audio > .mute.muted {
|
||||
}
|
||||
|
||||
.block-media > .bottom > .audio > .volume {
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: auto;
|
||||
height: 100%;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.block-media > .bottom > .fullscreen {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: 2rem;
|
||||
line-height: 2rem;
|
||||
text-align: center;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
// Block: Media > Audio
|
||||
// --------------------------------------------------------------------------------
|
||||
|
||||
+252
@@ -17,6 +17,255 @@ async function xmr_media_lazyload_initialize() {
|
||||
}
|
||||
}
|
||||
|
||||
async function xmr_initialize_player(el) {
|
||||
let media = el.querySelector("video");
|
||||
if (!media)
|
||||
el.querySelector("audio");
|
||||
|
||||
// Play/Pause
|
||||
let play_pause = el.querySelector(".bottom > .play");
|
||||
play_pause.update = function() {
|
||||
if (media.paused) {
|
||||
play_pause.classList.remove("playing");
|
||||
play_pause.innerText = "⏵";
|
||||
} else if (media.error || media.ended) {
|
||||
play_pause.classList.remove("playing");
|
||||
play_pause.innerText = "⏹";
|
||||
} else {
|
||||
play_pause.classList.add("playing");
|
||||
play_pause.innerText = "⏸";
|
||||
}
|
||||
}
|
||||
play_pause.addEventListener("click", () => {
|
||||
if (media.paused) {
|
||||
media.play();
|
||||
} else {
|
||||
media.pause();
|
||||
}
|
||||
});
|
||||
media.addEventListener("play", () => { play_pause.update(); });
|
||||
media.addEventListener("pause", () => { play_pause.update(); });
|
||||
media.addEventListener("ended", () => { play_pause.update(); });
|
||||
media.addEventListener("error", () => { play_pause.update(); });
|
||||
play_pause.update();
|
||||
|
||||
// Mute
|
||||
let audio_mute = el.querySelector(".bottom > .audio > .mute");
|
||||
audio_mute.update = function() {
|
||||
if (media.muted) {
|
||||
audio_mute.classList.add("muted");
|
||||
audio_mute.innerText = "🔇";
|
||||
} else {
|
||||
audio_mute.classList.remove("muted");
|
||||
if (media.volume > 0.5) {
|
||||
audio_mute.innerText = "🔊";
|
||||
} else if (media.volume > 0.125) {
|
||||
audio_mute.innerText = "🔉";
|
||||
} else {
|
||||
audio_mute.innerText = "🔈";
|
||||
}
|
||||
}
|
||||
}
|
||||
audio_mute.addEventListener("click", () => {
|
||||
media.muted = !media.muted;
|
||||
});
|
||||
media.addEventListener("volumechange", () => {
|
||||
audio_mute.update();
|
||||
});
|
||||
audio_mute.update();
|
||||
|
||||
// Volume
|
||||
let audio_volume = el.querySelector(".bottom > .audio > .volume");
|
||||
audio_volume.min = 0;
|
||||
audio_volume.max = 2147483647;
|
||||
audio_volume.update = function() {
|
||||
let min = parseInt(audio_volume.min, 10);
|
||||
let max = parseInt(audio_volume.max, 10);
|
||||
let dlt = max - min;
|
||||
audio_volume.value = (media.volume * dlt) + min;
|
||||
if (media.muted) {
|
||||
audio_volume.classList.add("muted");
|
||||
} else {
|
||||
audio_volume.classList.remove("muted");
|
||||
}
|
||||
}
|
||||
audio_volume.addEventListener("input", () => {
|
||||
let min = parseInt(audio_volume.min, 10);
|
||||
let max = parseInt(audio_volume.max, 10);
|
||||
let val = parseInt(audio_volume.value, 10);
|
||||
let dlt = max - min;
|
||||
let vol = (val + min) / dlt;
|
||||
media.volume = vol;
|
||||
media.muted = (media.volume < 0.01);
|
||||
});
|
||||
media.addEventListener("volumechange", () => { audio_volume.update(); });
|
||||
audio_volume.update();
|
||||
|
||||
// Time
|
||||
let playback_time = el.querySelector(".bottom > .playback > .time");
|
||||
playback_time.update = function() {
|
||||
let duration_text = "";
|
||||
let need_hour = false;
|
||||
if (isNaN(media.duration)) {
|
||||
duration_text = "Error";
|
||||
} else if (!isFinite(media.duration)) {
|
||||
duration_text = "Unknown";
|
||||
} else {
|
||||
let dur = media.duration;
|
||||
let s = dur % 60;
|
||||
let m0 = (dur - s) / 60;
|
||||
let m = m0 % 60;
|
||||
let h = (m0 - m) / 60;
|
||||
|
||||
let ss = Math.floor(s).toString().padStart(2, '0');
|
||||
let ms = Math.floor(m).toString().padStart(2, '0');
|
||||
|
||||
if (h > 0) {
|
||||
duration_text = `${h}:${ms}:${ss}`;
|
||||
need_hour = true;
|
||||
} else {
|
||||
duration_text = `${ms}:${ss}`;
|
||||
}
|
||||
}
|
||||
|
||||
let current_text = "";
|
||||
{
|
||||
let dur = media.currentTime;
|
||||
let s = dur % 60;
|
||||
let m0 = (dur - s) / 60;
|
||||
let m = m0 % 60;
|
||||
let h = (m0 - m) / 60;
|
||||
|
||||
let ss = Math.floor(s).toString().padStart(2, '0');
|
||||
let ms = Math.floor(m).toString().padStart(2, '0');
|
||||
|
||||
if (need_hour) {
|
||||
current_text = `${h}:${ms}:${ss}`;
|
||||
} else {
|
||||
current_text = `${ms}:${ss}`;
|
||||
}
|
||||
}
|
||||
|
||||
playback_time.innerText = `${current_text} / ${duration_text}`;
|
||||
}
|
||||
media.addEventListener("timeupdate", () => { playback_time.update(); });
|
||||
media.addEventListener("loadedmetadata", () => { playback_time.update(); });
|
||||
playback_time.update();
|
||||
|
||||
// Progress
|
||||
let playback_progress = el.querySelector(".bottom > .playback > .progress");
|
||||
playback_progress.update = function() {
|
||||
playback_progress.disabled = !media.seekable;
|
||||
playback_progress.min = 0;
|
||||
playback_progress.max = media.duration * 100;
|
||||
playback_progress.value = media.currentTime * 100;
|
||||
}
|
||||
playback_progress.addEventListener("input", () => {
|
||||
media.currentTime = parseInt(playback_progress.value, 10) / 100;
|
||||
});
|
||||
media.addEventListener("timeupdate", () => { playback_progress.update(); });
|
||||
media.addEventListener("loadedmetadata", () => { playback_progress.update(); });
|
||||
playback_progress.update();
|
||||
|
||||
// Fullscreen
|
||||
let fullscreen = el.querySelector(".fullscreen");
|
||||
fullscreen.update = function() {
|
||||
if (document.fullscreenElement) {
|
||||
fullscreen.innerText = "⬚";
|
||||
} else {
|
||||
fullscreen.innerText = "⛶";
|
||||
}
|
||||
};
|
||||
fullscreen.addEventListener("click", () => {
|
||||
if (document.fullscreenElement) {
|
||||
document.exitFullscreen();
|
||||
} else {
|
||||
el.requestFullscreen();
|
||||
}
|
||||
});
|
||||
el.addEventListener("fullscreenchange", (ev) => {
|
||||
fullscreen.update();
|
||||
});
|
||||
fullscreen.update();
|
||||
|
||||
// Variants
|
||||
let variants = el.querySelector(".variant");
|
||||
variants.addEventListener("input", () => {
|
||||
media.pause();
|
||||
play_pause.update();
|
||||
media.src = variants.value;
|
||||
});
|
||||
variants.addEventListener("value", () => {
|
||||
media.pause();
|
||||
play_pause.update();
|
||||
media.src = variants.value;
|
||||
});
|
||||
function createVariants(el, key, value) {
|
||||
if (typeof(value) === "object") {
|
||||
for (let k in value) {
|
||||
let v = value[k];
|
||||
createVariants(el, `${key} / ${k}`, v);
|
||||
}
|
||||
} else if (typeof(value) === "string") {
|
||||
let option = document.createElement("option");
|
||||
option.value = value;
|
||||
option.textContent = key;
|
||||
el.appendChild(option);
|
||||
} else {
|
||||
el.appendChild(document.createElement(typeof(value)));
|
||||
}
|
||||
}
|
||||
for (let k in el.variants) {
|
||||
let v = el.variants[k];
|
||||
createVariants(variants, k, v);
|
||||
}
|
||||
variants.value = media.src = variants.querySelectorAll("option")[0].value;
|
||||
|
||||
// Media Play/Pause by click
|
||||
media.addEventListener("click", () => {
|
||||
play_pause.click();
|
||||
})
|
||||
|
||||
// Hide/Show controls
|
||||
el.showOverlay = function() {
|
||||
el.cancelHideOverlay();
|
||||
el.classList.remove("hide");
|
||||
}
|
||||
el.hideOverlay = function() {
|
||||
if (!media.paused && !media.ended && !media.error) {
|
||||
el.classList.add("hide");
|
||||
}
|
||||
}
|
||||
el.delayHideOverlay = function() {
|
||||
el.cancelHideOverlay();
|
||||
el.timer = setTimeout(() => { el.hideOverlay(); }, 2500);
|
||||
}
|
||||
el.cancelHideOverlay = function() {
|
||||
if (el.timer) {
|
||||
clearTimeout(el.timer);
|
||||
}
|
||||
}
|
||||
el.addEventListener("mousemove", () => {
|
||||
el.showOverlay();
|
||||
if (!media.paused && !media.ended && !media.error) {
|
||||
el.delayHideOverlay();
|
||||
}
|
||||
});
|
||||
el.hideOverlay();
|
||||
}
|
||||
|
||||
async function xmr_initialize_players() {
|
||||
// Figure out what to initialize.
|
||||
let elements = document.querySelectorAll(".block-media.player");
|
||||
let stack = Array.from(elements);
|
||||
console.debug("Initializing " + stack.length + " players...");
|
||||
|
||||
// Add a lazyloading handler to all entries.
|
||||
for (let el of stack) {
|
||||
xmr_initialize_player(el);
|
||||
}
|
||||
}
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
@@ -27,4 +276,7 @@ async function xmr_media_lazyload_initialize() {
|
||||
|
||||
// Lazily load Media
|
||||
xmr_media_lazyload_initialize();
|
||||
|
||||
// Initialize media player.
|
||||
xmr_initialize_players();
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user