Fixes for cross origin content policy

This commit is contained in:
David Halls
2023-11-05 15:39:21 +00:00
parent fa19f367b3
commit 6435207d93
9 changed files with 105 additions and 31 deletions

View File

@@ -0,0 +1 @@
../ffmpeg.js/ffmpeg-worker-dash.worker.js

View File

@@ -0,0 +1 @@
../ffmpeg.js/ffmpeg-worker-hls.worker.js

View File

@@ -26,6 +26,7 @@ export class MuxReceiver extends EventTarget {
type: 'run',
arguments: [
'-seekable', '0',
//'-loglevel', 'debug',
...ffmpeg_args,
...(protocol === 'dash' ? [
'-f', 'dash', // use dash encoder
@@ -34,7 +35,7 @@ export class MuxReceiver extends EventTarget {
'-streaming', '1', // fragment data
'-dash_segment_type', 'webm', // container type
...protocol_args,
'/outbound/output.mpd'
`/outbound/output${Math.random()}.mpd`
] : [
'-f', 'hls', // use hls encoder
'-hls_time', '2', // 2 second HLS chunks
@@ -42,8 +43,8 @@ export class MuxReceiver extends EventTarget {
'-hls_list_size', '2', // two chunks in the list at a time
'-hls_flags', 'split_by_time', // if you don't have < 2s keyframes
...protocol_args,
'/outbound/output.m3u8' // path to media playlist file in virtual FS,
// must be under /outbound
`/outbound/output${Math.random()}.m3u8` // path to media playlist file in virtual FS,
// must be under /outbound
])
],
MEMFS: [
@@ -82,16 +83,8 @@ export class MuxReceiver extends EventTarget {
}}));
break;
case 'upload': {
const reader = msg.stream.getReader();
const chunks = [];
while (true) {
const { value, done } = await reader.read();
if (done) {
break;
}
chunks.push(value);
}
console.log('upload', msg.url, new Blob(chunks));
this.dispatchEvent(new CustomEvent('message', { detail: msg }));
break;
}
}
};

32
site/poster.html Normal file
View File

@@ -0,0 +1,32 @@
<html>
<head>
<script>
window.addEventListener('message', async e => {
const msg = e.data;
const reader = msg.options.stream.getReader();
const chunks = [];
while (true) {
const { value, done } = await reader.read();
if (done) {
break;
}
chunks.push(value);
}
delete msg.options.stream;
msg.options.body = new Blob(chunks);
fetch(msg.url, msg.options).then(response => {
//check_exit();
// note: with no-cors, response is opaque and ok will always be false
if (!response.ok && (msg.options.mode !== 'no-cors')) {
console.error("RESPONSE NOT OK", msg.url, response);
}
}).catch (err => {
//check_exit();
console.error("REQUEST ERROR", msg.url, err);
});
});
</script>
</head>
<body>
</body>
</html>

24
site/serve.json Normal file
View File

@@ -0,0 +1,24 @@
{
"headers": [
{
"source": "**",
"headers": [{
"key": "Cross-Origin-Opener-Policy",
"value": "same-origin"
}, {
"key": "Cross-Origin-Embedder-Policy",
"value": "credentialless"
}]
},
{
"source": "../**",
"headers": [{
"key": "Cross-Origin-Opener-Policy",
"value": "same-origin"
}, {
"key": "Cross-Origin-Embedder-Policy",
"value": "credentialless"
}]
}
]
}

View File

@@ -10,6 +10,7 @@
<script type="module" src="./streamana.js"></script>
</head>
<body class="d-flex flex-column vh-100 d-none">
<iframe credentialless id="poster" src="poster.html" width=0 height=0></iframe>
<div id="busy" class="busy"></div>
<nav id="nav" class="navbar navbar-light bg-light flex-grow-0">
<div class="container-fluid">
@@ -66,7 +67,7 @@
<label for="zoom-video" class="form-check-label">Minimize vertical bars in local video display</label>
</div>
<div class="pt-4">
<label class="form-label">Ingestion protocol</label>
<div class="form-label">Ingestion protocol</div>
<div>
<div class="form-check form-check-inline">
<input id="protocol-hls" name="protocol" class="form-check-input" type="radio" value="ffmpeg-worker-hls.js">
@@ -79,7 +80,7 @@
</div>
</div>
<div class="pt-4">
<label class="form-label">Request method</label>
<div class="form-label">Request method</div>
<div>
<div class="form-check form-check-inline">
<input id="request-post" name="request-method" class="form-check-input" type="radio" value="POST">
@@ -92,7 +93,7 @@
</div>
</div>
<div class="pt-4">
<label class="form-label">Cross-Origin Resource Sharing (CORS)</label>
<div class="form-label">Cross-Origin Resource Sharing (CORS)</div>
<div>
<div class="form-check form-check-inline">
<input id="request-cors" name="request-mode" class="form-check-input" type="radio" value="cors">
@@ -109,7 +110,7 @@
</div>
</div>
<div class="pt-4">
<label class="form-label">Encoder Preference</label>
<div class="form-label">Encoder Preference</div>
<div>
<div class="form-check form-check-inline">
<input id="prefer-mediarecorder" name="preferred-encoder" class="form-check-input" type="radio" value="mediarecorder">

View File

@@ -157,6 +157,8 @@ if (localStorage.getItem('streamana-encoder-preference') === 'webcodecs') {
prefer_mediarecorder_el.checked = true;
}
const poster_el = document.getElementById('poster');
let streamer_config;
let video_config;
const video_configs = new Map();
@@ -267,7 +269,7 @@ await set_ingestion();
document.body.classList.remove('d-none');
document.documentElement.classList.remove('busy');
let streamer;
let streamer, audio_context;
async function start() {
const ingestion_url = ingestion_url_el.value.trim();
@@ -338,7 +340,7 @@ async function start() {
const zoom_video = zoom_video_el.checked;
const lock_portrait = /*screen.orientation.type.startsWith('portrait') &&*/ lock_portrait_el.checked;
let audio_context, video_el, video_track, silence, audio_source, audio_dest, gl_canvas, canvas_stream, done = false;
let video_el, video_track, silence, audio_source, audio_dest, gl_canvas, canvas_stream, done = false;
function cleanup(err) {
if (err) {
@@ -397,9 +399,8 @@ async function start() {
silence.stop();
}
if (audio_context) {
audio_context.suspend().then(function () {
audio_context.close();
});;
// Chrome doesn't GC AudioContexts so reuse a single instance.
audio_context.suspend();
}
if (video_track) {
video_track.stop();
@@ -652,10 +653,19 @@ async function start() {
}
try {
// Safari requires us to create and resume an AudioContext in the click handler
// and doesn't track async calls.
audio_context = new AudioContext();
audio_context.resume();
if (!audio_context) {
audio_context = new AudioContext();
}
try {
audio_context.resume();
} catch {
// Safari requires us to create and resume an AudioContext
// in the click handler and doesn't track async calls.
audio_context.close();
audio_context = new AudioContext();
audio_context.resume();
}
// create video element which will be used for grabbing the frames to
// write to a canvas so we can apply webgl shaders
@@ -755,7 +765,8 @@ async function start() {
streamer_config,
lock_portrait,
{ method, mode },
prefer_webcodecs_el.checked);
prefer_webcodecs_el.checked,
poster_el.contentWindow);
streamer.addEventListener('run', () => console.log('Streamer running'));
streamer.addEventListener('exit', ev => {
const msg = `Streamer exited with status ${ev.detail.code}`;

View File

@@ -60,7 +60,7 @@ export function get_default_config_from_url(ffmpeg_lib_url) {
}
export class Streamer extends EventTarget {
constructor(stream, audio_context, base_url, config, rotate, request_options, prefer_webcodecs) {
constructor(stream, audio_context, base_url, config, rotate, request_options, prefer_webcodecs, poster) {
super();
this.stream = stream;
this.audio_context = audio_context;
@@ -76,6 +76,7 @@ export class Streamer extends EventTarget {
this.sending = false;
this.started = false;
this.prefer_webcodecs = prefer_webcodecs;
this.poster = poster;
}
async start() {
@@ -168,7 +169,7 @@ export class Streamer extends EventTarget {
this.config.ffmpeg.audio.codec, // re-encode audio
'-b:a', this.config.audio.bitrate.toString() // set audio bitrate
],
base_url: this.base_url,
base_url: 'postMessage:', //this.base_url,
protocol: this.config.protocol,
protocol_args: [],
request_options: this.request_options
@@ -295,6 +296,11 @@ export class Streamer extends EventTarget {
}
this.dispatchEvent(new CustomEvent(msg.type, { detail: { code: msg.code } }));
break;
case 'upload':
msg.url = this.base_url + msg.url.split(':')[1];
this.poster.postMessage(msg, '*', msg.transfer);
break;
}
});
}
@@ -397,6 +403,11 @@ export class Streamer extends EventTarget {
}
this.dispatchEvent(new CustomEvent(msg.type, { detail: { code: msg.code } }));
break;
case 'upload':
msg.url = this.base_url + msg.url.split(':')[1];
this.poster.postMessage(msg, '*', msg.transfer);
break;
}
};
@@ -457,7 +468,7 @@ export class Streamer extends EventTarget {
this.worker.postMessage({
type: 'start',
webm_metadata: {
max_segment_duration: BigInt(1000000000),
max_cluster_duration: BigInt(1000000000),
video: {
width: video_settings.width,
height: video_settings.height,