Fix HLS go live

This commit is contained in:
David Halls
2021-10-14 18:04:58 +01:00
parent ff34cca63d
commit 50bd476501
3 changed files with 57 additions and 33 deletions

View File

@@ -29,11 +29,12 @@ export class GlCanvas extends Canvas {
}
}), options);
this.update_limiter = new UpdateLimiter();
this.destroyed = false;
}
// Allow rendering loop to be driven externally (e.g. by the audio encoder)
// to avoid requestAnimationFrame (or indeed setInterval) throttling.
onLoop() {
if (this.update_limiter.check()) {
if (this.update_limiter.check() && !this.destroyed) {
const now = Date.now();
this.checkRender();
// Make sure we don't hog the main thread. Software rendering will take
@@ -50,14 +51,25 @@ export class GlCanvas extends Canvas {
}
// Prevent errors after destruction
destroy() {
super.destroy();
this.destroyed = true;
if (this.gl) {
super.destroy();
}
this.uniforms = {
createTexture() {
return {};
},
create() {}
create() {},
update() {}
};
this.textures = {
createOrUpdate() {
return {
then() {}
};
},
values: {}
};
this.textures = {};
this.buffers = {
values: {}
};

View File

@@ -36,30 +36,34 @@ const initial_ffmpeg_lib_url = (localStorage.getItem('streamana-ffmpeg-lib-url')
if (initial_ffmpeg_lib_url) {
ffmpeg_lib_url_el.value = initial_ffmpeg_lib_url;
}
ffmpeg_lib_url_el.addEventListener('input', function () {
localStorage.setItem('streamana-ffmpeg-lib-url', this.value);
if (this.value.trim()) {
ffmpeg_lib_url_el.addEventListener('change', function () {
const value = this.value.trim();
localStorage.setItem('streamana-ffmpeg-lib-url', value);
if (value) {
protocol_hls_el.disabled = true;
protocol_dash_el.disabled = true;
} else {
protocol_hls_el.disabled = false;
protocol_dash_el.disabled = false;
}
set_ingestion();
});
const zoom_video_el = document.getElementById('zoom-video');
zoom_video_el.checked = !!localStorage.getItem('streamana-zoom-video');
zoom_video_el.addEventListener('input', function () {
zoom_video_el.addEventListener('change', function () {
localStorage.setItem('streamana-zoom-video', this.checked ? 'true' : '');
});
const lock_portrait_el = document.getElementById('lock-portrait');
lock_portrait_el.checked = !!localStorage.getItem('streamana-lock-portrait');
lock_portrait_el.addEventListener('input', function () {
lock_portrait_el.addEventListener('change', function () {
localStorage.setItem('streamana-lock-portrait', this.checked ? 'true' : '');
});
const greyscale_el = document.getElementById('greyscale');
greyscale_el.checked = !!localStorage.getItem('streamana-greyscale');
greyscale_el.addEventListener('input', function () {
greyscale_el.addEventListener('change', function () {
localStorage.setItem('streamana-greyscale', this.checked ? 'true' : '');
});
@@ -116,13 +120,8 @@ let streamer_config;
let video_config;
const video_configs = new Map();
function set_ingestion() {
const ffmpeg_lib_url = ffmpeg_lib_url_el.value.trim() ||
ffmpeg_lib_url_el.placeholder.trim();
streamer_config = get_default_config_from_url(ffmpeg_lib_url);
if (streamer_config.protocol === 'dash') {
function set_ingestion_protocol(protocol) {
if (protocol === 'dash') {
protocol_hls_el.checked = false;
protocol_dash_el.checked = true;
ffmpeg_lib_url_el.placeholder = protocol_dash_el.value;
@@ -131,7 +130,20 @@ function set_ingestion() {
protocol_dash_el.checked = false;
ffmpeg_lib_url_el.placeholder = protocol_hls_el.value;
}
}
set_ingestion_protocol(localStorage.getItem('streamana-ingestion-protocol'));
async function set_ingestion() {
const ffmpeg_lib_url = ffmpeg_lib_url_el.value.trim() ||
ffmpeg_lib_url_el.placeholder.trim();
streamer_config = get_default_config_from_url(ffmpeg_lib_url);
set_ingestion_protocol(streamer_config.protocol);
localStorage.setItem('streamana-ingestion-protocol', streamer_config.protocol);
video_config = null;
let preferred_resolution = localStorage.getItem('streamana-resolution');
if (preferred_resolution) {
video_config = await max_video_config({
@@ -163,14 +175,14 @@ function set_ingestion() {
}
}
set_ingestion();
await set_ingestion();
protocol_hls_el.addEventListener('input', function () {
protocol_hls_el.addEventListener('change', function () {
ffmpeg_lib_url_el.placeholder = protocol_hls_el.value;
set_ingestion();
});
protocol_dash_el.addEventListener('input', function () {
protocol_dash_el.addEventListener('change', function () {
ffmpeg_lib_url_el.placeholder = protocol_dash_el.value;
set_ingestion();
});
@@ -259,7 +271,7 @@ async function start() {
camera_icon_el.classList.add('off');
}
camera_el.addEventListener('click', camera_save);
greyscale_el.removeEventListener('input', greyscale);
greyscale_el.removeEventListener('change', greyscale);
camera_swap_el.classList.add('d-none');
camera_swap_el.removeEventListener('click', about_face);
canvas_el_parent.classList.add('mx-auto');
@@ -321,8 +333,8 @@ async function start() {
lock_portrait_el.disabled = false;
zoom_video_el.disabled = false;
resolution_el.disabled = false;
protocol_hls_el.disabled = false;
protocol_dash_el.disabled = false;
protocol_hls_el.disabled = ffmpeg_lib_url_el.value.trim();
protocol_dash_el.disabled = ffmpeg_lib_url_el.value.trim();;
waiting_el.classList.add('d-none');
canvas_el.classList.add('d-none');
}
@@ -419,8 +431,8 @@ async function start() {
width: video_config.width,
height: video_config.height,
frameRate: {
ideal: streamer.config.video.framerate,
max: streamer.config.video.framerate
ideal: streamer_config.video.framerate,
max: streamer_config.video.framerate
},
facingMode: requested_facing_mode
};
@@ -578,7 +590,7 @@ async function start() {
// tell shader whether to greyscale
gl_canvas.setUniform('u_greyscale', greyscale_el.checked);
greyscale_el.addEventListener('input', greyscale);
greyscale_el.addEventListener('change', greyscale);
// tell shader camera hasn't started
gl_canvas.setUniform('u_active', false);
@@ -616,7 +628,7 @@ async function start() {
// capture video from the canvas
// Note: Safari on iOS doesn't get any data, might be related to
// https://bugs.webkit.org/show_bug.cgi?id=181663
canvas_stream = canvas_el.captureStream(target_frame_rate);
canvas_stream = canvas_el.captureStream(streamer_config.video.framerate);
// add audio to canvas stream
audio_dest = audio_context.createMediaStreamDestination();

View File

@@ -4,7 +4,7 @@ import { MuxReceiver } from './mux-receiver.js';
const key_frame_interval = 3;
export function get_default_config_from_url(ffmpeg_lib_url) {
const protocol = url.indexOf('worker-dash') >= 0 ? 'dash' : 'hls';
const protocol = ffmpeg_lib_url.indexOf('worker-dash') >= 0 ? 'dash' : 'hls';
return {
ffmpeg_lib_url,
protocol,
@@ -14,7 +14,7 @@ export function get_default_config_from_url(ffmpeg_lib_url) {
},
audio: {
bitrate: 128 * 1000
}
},
media_recorder: {
video: {
codec: protocol === 'dash' ? 'vp9' : 'H264',
@@ -43,7 +43,7 @@ export function get_default_config_from_url(ffmpeg_lib_url) {
codec: protocol === 'dash' ? 'V_VP9' : 'V_MPEG4/ISO/AVC'
},
audio: {
codec: 'A_OPUS'
codec: 'A_OPUS',
bit_depth: 0 // 32 for pcm */
}
}
@@ -157,7 +157,7 @@ export class Streamer extends EventTarget {
video_codec === 'copy' ?
['-c:v', 'copy'] : // pass through the video data (no decoding or encoding)
['-c:v', this.config.ffmpeg.video.codec, // re-encode video
'-b:v', this.config.video.bitrate.toString()]) // set video bitrate
'-b:v', this.config.video.bitrate.toString()]), // set video bitrate
...this.ffmpeg_metadata,
...(audio_codec === this.config.ffmpeg.audio.codec ||
audio_codec === 'copy' ?
@@ -262,7 +262,7 @@ export class Streamer extends EventTarget {
const msg = e.detail;
switch (msg.type) {
case 'ready':
this.receiver.start(receiver_args(video_codec, audio_codec));
this.receiver.start(this.receiver_args(video_codec, audio_codec));
break;
case 'error':
@@ -468,7 +468,7 @@ export class Streamer extends EventTarget {
},
webm_receiver: './mux-receiver.js',
webm_receiver_data: { name: 'stream1' },
...receiver_args(video_codec, audio_codec)
...this.receiver_args(video_codec, audio_codec)
});
}