mirror of
https://github.com/davedoesdev/streamana.git
synced 2025-10-04 21:22:40 +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',
|
type: 'run',
|
||||||
arguments: [
|
arguments: [
|
||||||
'-seekable', '0',
|
'-seekable', '0',
|
||||||
|
//'-loglevel', 'debug',
|
||||||
...ffmpeg_args,
|
...ffmpeg_args,
|
||||||
...(protocol === 'dash' ? [
|
...(protocol === 'dash' ? [
|
||||||
'-f', 'dash', // use dash encoder
|
'-f', 'dash', // use dash encoder
|
||||||
@@ -34,7 +35,7 @@ export class MuxReceiver extends EventTarget {
|
|||||||
'-streaming', '1', // fragment data
|
'-streaming', '1', // fragment data
|
||||||
'-dash_segment_type', 'webm', // container type
|
'-dash_segment_type', 'webm', // container type
|
||||||
...protocol_args,
|
...protocol_args,
|
||||||
'/outbound/output.mpd'
|
`/outbound/output${Math.random()}.mpd`
|
||||||
] : [
|
] : [
|
||||||
'-f', 'hls', // use hls encoder
|
'-f', 'hls', // use hls encoder
|
||||||
'-hls_time', '2', // 2 second HLS chunks
|
'-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_list_size', '2', // two chunks in the list at a time
|
||||||
'-hls_flags', 'split_by_time', // if you don't have < 2s keyframes
|
'-hls_flags', 'split_by_time', // if you don't have < 2s keyframes
|
||||||
...protocol_args,
|
...protocol_args,
|
||||||
'/outbound/output.m3u8' // path to media playlist file in virtual FS,
|
`/outbound/output${Math.random()}.m3u8` // path to media playlist file in virtual FS,
|
||||||
// must be under /outbound
|
// must be under /outbound
|
||||||
])
|
])
|
||||||
],
|
],
|
||||||
MEMFS: [
|
MEMFS: [
|
||||||
@@ -82,16 +83,8 @@ export class MuxReceiver extends EventTarget {
|
|||||||
}}));
|
}}));
|
||||||
break;
|
break;
|
||||||
case 'upload': {
|
case 'upload': {
|
||||||
const reader = msg.stream.getReader();
|
this.dispatchEvent(new CustomEvent('message', { detail: msg }));
|
||||||
const chunks = [];
|
break;
|
||||||
while (true) {
|
|
||||||
const { value, done } = await reader.read();
|
|
||||||
if (done) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
chunks.push(value);
|
|
||||||
}
|
|
||||||
console.log('upload', msg.url, new Blob(chunks));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
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>
|
<script type="module" src="./streamana.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body class="d-flex flex-column vh-100 d-none">
|
<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>
|
<div id="busy" class="busy"></div>
|
||||||
<nav id="nav" class="navbar navbar-light bg-light flex-grow-0">
|
<nav id="nav" class="navbar navbar-light bg-light flex-grow-0">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
@@ -66,7 +67,7 @@
|
|||||||
<label for="zoom-video" class="form-check-label">Minimize vertical bars in local video display</label>
|
<label for="zoom-video" class="form-check-label">Minimize vertical bars in local video display</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="pt-4">
|
<div class="pt-4">
|
||||||
<label class="form-label">Ingestion protocol</label>
|
<div class="form-label">Ingestion protocol</div>
|
||||||
<div>
|
<div>
|
||||||
<div class="form-check form-check-inline">
|
<div class="form-check form-check-inline">
|
||||||
<input id="protocol-hls" name="protocol" class="form-check-input" type="radio" value="ffmpeg-worker-hls.js">
|
<input id="protocol-hls" name="protocol" class="form-check-input" type="radio" value="ffmpeg-worker-hls.js">
|
||||||
@@ -79,7 +80,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="pt-4">
|
<div class="pt-4">
|
||||||
<label class="form-label">Request method</label>
|
<div class="form-label">Request method</div>
|
||||||
<div>
|
<div>
|
||||||
<div class="form-check form-check-inline">
|
<div class="form-check form-check-inline">
|
||||||
<input id="request-post" name="request-method" class="form-check-input" type="radio" value="POST">
|
<input id="request-post" name="request-method" class="form-check-input" type="radio" value="POST">
|
||||||
@@ -92,7 +93,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="pt-4">
|
<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>
|
||||||
<div class="form-check form-check-inline">
|
<div class="form-check form-check-inline">
|
||||||
<input id="request-cors" name="request-mode" class="form-check-input" type="radio" value="cors">
|
<input id="request-cors" name="request-mode" class="form-check-input" type="radio" value="cors">
|
||||||
@@ -109,7 +110,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="pt-4">
|
<div class="pt-4">
|
||||||
<label class="form-label">Encoder Preference</label>
|
<div class="form-label">Encoder Preference</div>
|
||||||
<div>
|
<div>
|
||||||
<div class="form-check form-check-inline">
|
<div class="form-check form-check-inline">
|
||||||
<input id="prefer-mediarecorder" name="preferred-encoder" class="form-check-input" type="radio" value="mediarecorder">
|
<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;
|
prefer_mediarecorder_el.checked = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const poster_el = document.getElementById('poster');
|
||||||
|
|
||||||
let streamer_config;
|
let streamer_config;
|
||||||
let video_config;
|
let video_config;
|
||||||
const video_configs = new Map();
|
const video_configs = new Map();
|
||||||
@@ -267,7 +269,7 @@ await set_ingestion();
|
|||||||
document.body.classList.remove('d-none');
|
document.body.classList.remove('d-none');
|
||||||
document.documentElement.classList.remove('busy');
|
document.documentElement.classList.remove('busy');
|
||||||
|
|
||||||
let streamer;
|
let streamer, audio_context;
|
||||||
|
|
||||||
async function start() {
|
async function start() {
|
||||||
const ingestion_url = ingestion_url_el.value.trim();
|
const ingestion_url = ingestion_url_el.value.trim();
|
||||||
@@ -338,7 +340,7 @@ async function start() {
|
|||||||
|
|
||||||
const zoom_video = zoom_video_el.checked;
|
const zoom_video = zoom_video_el.checked;
|
||||||
const lock_portrait = /*screen.orientation.type.startsWith('portrait') &&*/ lock_portrait_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) {
|
function cleanup(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
@@ -397,9 +399,8 @@ async function start() {
|
|||||||
silence.stop();
|
silence.stop();
|
||||||
}
|
}
|
||||||
if (audio_context) {
|
if (audio_context) {
|
||||||
audio_context.suspend().then(function () {
|
// Chrome doesn't GC AudioContexts so reuse a single instance.
|
||||||
audio_context.close();
|
audio_context.suspend();
|
||||||
});;
|
|
||||||
}
|
}
|
||||||
if (video_track) {
|
if (video_track) {
|
||||||
video_track.stop();
|
video_track.stop();
|
||||||
@@ -652,10 +653,19 @@ async function start() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Safari requires us to create and resume an AudioContext in the click handler
|
if (!audio_context) {
|
||||||
// and doesn't track async calls.
|
audio_context = new AudioContext();
|
||||||
audio_context = new AudioContext();
|
}
|
||||||
audio_context.resume();
|
|
||||||
|
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
|
// create video element which will be used for grabbing the frames to
|
||||||
// write to a canvas so we can apply webgl shaders
|
// write to a canvas so we can apply webgl shaders
|
||||||
@@ -755,7 +765,8 @@ async function start() {
|
|||||||
streamer_config,
|
streamer_config,
|
||||||
lock_portrait,
|
lock_portrait,
|
||||||
{ method, mode },
|
{ method, mode },
|
||||||
prefer_webcodecs_el.checked);
|
prefer_webcodecs_el.checked,
|
||||||
|
poster_el.contentWindow);
|
||||||
streamer.addEventListener('run', () => console.log('Streamer running'));
|
streamer.addEventListener('run', () => console.log('Streamer running'));
|
||||||
streamer.addEventListener('exit', ev => {
|
streamer.addEventListener('exit', ev => {
|
||||||
const msg = `Streamer exited with status ${ev.detail.code}`;
|
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 {
|
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();
|
super();
|
||||||
this.stream = stream;
|
this.stream = stream;
|
||||||
this.audio_context = audio_context;
|
this.audio_context = audio_context;
|
||||||
@@ -76,6 +76,7 @@ export class Streamer extends EventTarget {
|
|||||||
this.sending = false;
|
this.sending = false;
|
||||||
this.started = false;
|
this.started = false;
|
||||||
this.prefer_webcodecs = prefer_webcodecs;
|
this.prefer_webcodecs = prefer_webcodecs;
|
||||||
|
this.poster = poster;
|
||||||
}
|
}
|
||||||
|
|
||||||
async start() {
|
async start() {
|
||||||
@@ -168,7 +169,7 @@ export class Streamer extends EventTarget {
|
|||||||
this.config.ffmpeg.audio.codec, // re-encode audio
|
this.config.ffmpeg.audio.codec, // re-encode audio
|
||||||
'-b:a', this.config.audio.bitrate.toString() // set audio bitrate
|
'-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: this.config.protocol,
|
||||||
protocol_args: [],
|
protocol_args: [],
|
||||||
request_options: this.request_options
|
request_options: this.request_options
|
||||||
@@ -295,6 +296,11 @@ export class Streamer extends EventTarget {
|
|||||||
}
|
}
|
||||||
this.dispatchEvent(new CustomEvent(msg.type, { detail: { code: msg.code } }));
|
this.dispatchEvent(new CustomEvent(msg.type, { detail: { code: msg.code } }));
|
||||||
break;
|
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 } }));
|
this.dispatchEvent(new CustomEvent(msg.type, { detail: { code: msg.code } }));
|
||||||
break;
|
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({
|
this.worker.postMessage({
|
||||||
type: 'start',
|
type: 'start',
|
||||||
webm_metadata: {
|
webm_metadata: {
|
||||||
max_segment_duration: BigInt(1000000000),
|
max_cluster_duration: BigInt(1000000000),
|
||||||
video: {
|
video: {
|
||||||
width: video_settings.width,
|
width: video_settings.width,
|
||||||
height: video_settings.height,
|
height: video_settings.height,
|
||||||
|
Reference in New Issue
Block a user