mirror of
https://github.com/davedoesdev/streamana.git
synced 2025-10-23 13:33:44 +08:00
Display portrait video correctly
This commit is contained in:
7
site/example.css
Normal file
7
site/example.css
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
.portrait {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%,-50%) rotate(-90deg);
|
||||||
|
width: unset !important;
|
||||||
|
}
|
@@ -2,6 +2,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-+0n0xVW2eSR5OomGNYDnhzAbDsOXxcvSN1TPprVMTNDbiYZCxYbOOl7+AMvyTG2x" crossorigin="anonymous">
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-+0n0xVW2eSR5OomGNYDnhzAbDsOXxcvSN1TPprVMTNDbiYZCxYbOOl7+AMvyTG2x" crossorigin="anonymous">
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-gtEjrD/SeCtmISkJkNUaaKMoLD0//ElJ19smozuHV6z3Iehds+3Ulb9Bn9Plx0x4" crossorigin="anonymous"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-gtEjrD/SeCtmISkJkNUaaKMoLD0//ElJ19smozuHV6z3Iehds+3Ulb9Bn9Plx0x4" crossorigin="anonymous"></script>
|
||||||
|
<link href="./example.css" rel="stylesheet">
|
||||||
<script type="module" src="./example.js"></script>
|
<script type="module" src="./example.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -31,7 +32,7 @@
|
|||||||
<strong>An error occurred!</strong> See the Developer Console for details.
|
<strong>An error occurred!</strong> See the Developer Console for details.
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="position-relative">
|
<div class="position-relative h-100">
|
||||||
<canvas id="canvas" class="w-100" playsinline></canvas>
|
<canvas id="canvas" class="w-100" playsinline></canvas>
|
||||||
<div class="position-fixed top-50 start-50 translate-middle">
|
<div class="position-fixed top-50 start-50 translate-middle">
|
||||||
<div id="waiting" class="text-primary spinner-border d-none" role="status">
|
<div id="waiting" class="text-primary spinner-border d-none" role="status">
|
||||||
|
@@ -44,11 +44,11 @@ async function start() {
|
|||||||
|
|
||||||
go_live_el.disabled = true;
|
go_live_el.disabled = true;
|
||||||
waiting_el.classList.remove('d-none');
|
waiting_el.classList.remove('d-none');
|
||||||
const parent_el = canvas_el.parentNode;
|
const canvas_el_parent = canvas_el.parentNode;
|
||||||
parent_el.removeChild(canvas_el);
|
canvas_el_parent.removeChild(canvas_el);
|
||||||
canvas_el = canvas_proto.cloneNode();
|
canvas_el = canvas_proto.cloneNode();
|
||||||
canvas_el.classList.add('invisible');
|
canvas_el.classList.add('invisible');
|
||||||
parent_el.appendChild(canvas_el);
|
canvas_el_parent.appendChild(canvas_el);
|
||||||
|
|
||||||
if (error_alert_el.parentNode) {
|
if (error_alert_el.parentNode) {
|
||||||
error_alert_el_parent.removeChild(error_alert_el);
|
error_alert_el_parent.removeChild(error_alert_el);
|
||||||
@@ -146,13 +146,16 @@ async function start() {
|
|||||||
video_el.addEventListener('loadeddata', async function () {
|
video_el.addEventListener('loadeddata', async function () {
|
||||||
try {
|
try {
|
||||||
// make canvas same size as native video dimensions so every pixel is seen
|
// make canvas same size as native video dimensions so every pixel is seen
|
||||||
if (this.videoWidth > this.videoHeight) {
|
const portrait = this.videoHeight > this.videoWidth;
|
||||||
canvas_el.width = this.videoWidth;
|
if (portrait) {
|
||||||
canvas_el.height = this.videoHeight;
|
|
||||||
} else {
|
|
||||||
canvas_el.width = this.videoHeight;
|
canvas_el.width = this.videoHeight;
|
||||||
canvas_el.height = this.videoWidth;
|
canvas_el.height = this.videoWidth;
|
||||||
|
canvas_el.classList.add('portrait');
|
||||||
|
} else {
|
||||||
|
canvas_el.width = this.videoWidth;
|
||||||
|
canvas_el.height = this.videoHeight;
|
||||||
}
|
}
|
||||||
|
gl_canvas.setUniform('u_portrait', portrait);
|
||||||
|
|
||||||
// start the camera video
|
// start the camera video
|
||||||
this.play();
|
this.play();
|
||||||
@@ -169,6 +172,13 @@ async function start() {
|
|||||||
canvas_stream.addTrack(audio_tracks[0]);
|
canvas_stream.addTrack(audio_tracks[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function update() {
|
||||||
|
// update the canvas
|
||||||
|
if (gl_canvas.onLoop() && portrait) {
|
||||||
|
canvas_el.style.height = canvas_el_parent.clientWidth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// start HLS from the canvas stream to the ingestion URL
|
// start HLS from the canvas stream to the ingestion URL
|
||||||
hls = new HLS(canvas_stream, ingestion_url, ffmpeg_lib_url, frame_rate);
|
hls = new HLS(canvas_stream, ingestion_url, ffmpeg_lib_url, frame_rate);
|
||||||
hls.addEventListener('run', () => console.log('HLS running'));
|
hls.addEventListener('run', () => console.log('HLS running'));
|
||||||
@@ -186,11 +196,9 @@ async function start() {
|
|||||||
waiting_el.classList.add('d-none');
|
waiting_el.classList.add('d-none');
|
||||||
canvas_el.classList.remove('invisible');
|
canvas_el.classList.remove('invisible');
|
||||||
go_live_el.disabled = false;
|
go_live_el.disabled = false;
|
||||||
gl_canvas.onLoop();
|
update();
|
||||||
});
|
|
||||||
hls.addEventListener('update', () => {
|
|
||||||
gl_canvas.onLoop();
|
|
||||||
});
|
});
|
||||||
|
hls.addEventListener('update', update);
|
||||||
await hls.start();
|
await hls.start();
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
cleanup(ex);
|
cleanup(ex);
|
||||||
|
@@ -42,7 +42,9 @@ export class GlCanvas extends Canvas {
|
|||||||
// Better to "support" only hardware rendering (or very fast CPUs!), where
|
// Better to "support" only hardware rendering (or very fast CPUs!), where
|
||||||
// time to render each frame is only 1ms max.
|
// time to render each frame is only 1ms max.
|
||||||
this.update_limiter.threshold = (Date.now() - now) * 2;
|
this.update_limiter.threshold = (Date.now() - now) * 2;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
// Prevent errors after destruction
|
// Prevent errors after destruction
|
||||||
destroy() {
|
destroy() {
|
||||||
|
@@ -4,17 +4,16 @@ precision highp float;
|
|||||||
|
|
||||||
uniform sampler2D u_texture;
|
uniform sampler2D u_texture;
|
||||||
uniform vec2 u_resolution;
|
uniform vec2 u_resolution;
|
||||||
|
uniform bool u_portrait;
|
||||||
|
|
||||||
out vec4 colour;
|
out vec4 colour;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec2 coord;
|
vec2 coord;
|
||||||
// TODO: we should pass in whether to invert
|
if (u_portrait) {
|
||||||
ivec2 size = textureSize(u_texture, 0);
|
|
||||||
if (size.x > size.y) {
|
|
||||||
coord = gl_FragCoord.xy / u_resolution.xy;
|
|
||||||
} else {
|
|
||||||
coord = gl_FragCoord.yx / u_resolution.yx;
|
coord = gl_FragCoord.yx / u_resolution.yx;
|
||||||
|
} else {
|
||||||
|
coord = gl_FragCoord.xy / u_resolution.xy;
|
||||||
}
|
}
|
||||||
vec3 color = texture(u_texture, coord).rgb;
|
vec3 color = texture(u_texture, coord).rgb;
|
||||||
float grey = dot(color, vec3(0.299, 0.587, 0.114));
|
float grey = dot(color, vec3(0.299, 0.587, 0.114));
|
||||||
|
Reference in New Issue
Block a user