mirror of
https://github.com/davedoesdev/streamana.git
synced 2025-09-26 17:51:12 +08:00
Fixes for cross origin content policy
This commit is contained in:
Submodule ffmpeg.js updated: 7257e61560...7f3fe0e206
1
site/ffmpeg-worker-dash.worker.js
Symbolic link
1
site/ffmpeg-worker-dash.worker.js
Symbolic link
@@ -0,0 +1 @@
|
||||
../ffmpeg.js/ffmpeg-worker-dash.worker.js
|
1
site/ffmpeg-worker-hls.worker.js
Symbolic link
1
site/ffmpeg-worker-hls.worker.js
Symbolic link
@@ -0,0 +1 @@
|
||||
../ffmpeg.js/ffmpeg-worker-hls.worker.js
|
@@ -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
32
site/poster.html
Normal 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
24
site/serve.json
Normal 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"
|
||||
}]
|
||||
}
|
||||
]
|
||||
}
|
@@ -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">
|
||||
|
@@ -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}`;
|
||||
|
@@ -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,
|
||||
|
Reference in New Issue
Block a user