version 24.4 happy holidays

This commit is contained in:
steveseguin
2023-12-19 10:14:14 -05:00
parent d0e0d7e04d
commit 3230e961e3
21 changed files with 7037 additions and 1214 deletions

View File

@@ -26,221 +26,122 @@ Copyright (c) 2012-2020 [Muaz Khan](https://github.com/muaz-khan)
// *FILE HAS BEEN HEAVILY MODIFIED BY STEVE SEGUIN. ALL RIGHTS RESERVED WHERE APPLICABLE *
var CodecsHandler = (function() {
function preferCodec(sdp, codecName) {
if (codecName){
codecName = codecName.toLowerCase();
function preferCodec(sdp, codec, useRed=false, useUlpfec=false) {
if (codec){
codec = codec.toLowerCase();
}
var info = splitLines(sdp);
if (!info.videoCodecNumbers) {
return sdp;
} else if (codecName === 'vp8' && info.vp8LineNumber === info.videoCodecNumbers[0]) {
return sdp;
} else if (codecName === 'vp9' && info.vp9LineNumber === info.videoCodecNumbers[0]) {
return sdp;
} else if (codecName === 'h264' && info.h264LineNumber === info.videoCodecNumbers[0]) {
return sdp;
} else if (codecName === 'h265' && info.h265LineNumber === info.videoCodecNumbers[0]) {
return sdp;
} else if (codecName === 'av1' && info.av1LineNumber === info.videoCodecNumbers[0]) {
return sdp;
}
//} else if (codecName === 'red' && info.redLineNumber === info.videoCodecNumbers[0]) {
// return sdp;
//} else if (codecName === 'fec' && info.fecLineNumber === info.videoCodecNumbers[0]) {
// return sdp;
//}
sdp = preferCodecHelper(sdp, codecName, info);
return sdp;
}
function preferAudioCodec(sdp, codecName) {
if (codecName){
codecName = codecName.toLowerCase();
}
var info = splitAudioLines(sdp);
if (!info.audioCodecNumbers) {
return sdp;
} else if (codecName === 'opus' && info.opusLineNumber === info.audioCodecNumbers[0]) {
return sdp;
} else if (codecName === 'isac' && info.isacLineNumber === info.audioCodecNumbers[0]) {
return sdp;
} else if (codecName === 'g722' && info.g722LineNumber === info.audioCodecNumbers[0]) {
return sdp;
} else if (codecName === 'pcmu' && info.pcmuLineNumber === info.audioCodecNumbers[0]) {
return sdp;
} else if (codecName === 'pcma' && info.pcmaLineNumber === info.audioCodecNumbers[0]) {
return sdp;
} else if (codecName === 'red' && info.redLineNumber === info.audioCodecNumbers[0]) {
return sdp;
}
sdp = preferAudioCodecHelper(sdp, codecName, info);
return sdp;
}
function preferCodecHelper(sdp, codec, info) {
var preferCodecNumber = '';
if (codec === 'vp8') {
if (!info.vp8LineNumber) {
return sdp;
}
preferCodecNumber = info.vp8LineNumber;
} else if (codec === 'vp9') {
if (!info.vp9LineNumber) {
return sdp;
}
preferCodecNumber = info.vp9LineNumber;
} else if (codec === 'h264') {
if (!info.h264LineNumber) {
return sdp;
}
preferCodecNumber = info.h264LineNumber;
} else if (codec === 'h265') {
if (!info.h265LineNumber) {
return sdp;
}
preferCodecNumber = info.h265LineNumber;
} else if (codec === 'av1') {
if (!info.av1LineNumber) {
return sdp;
}
preferCodecNumber = info.av1LineNumber;
} else {
var info = splitLines(sdp);
if (!info.videoCodecNumbers) {
return sdp;
}
//} else if (codec === 'red') {
// if (!info.redLineNumber) {
// return sdp;
// }
// preferCodecNumber = info.redLineNumber;
//} else if (codec === 'fec') {
// if (!info.fecLineNumber) {
// return sdp;
// }
// preferCodecNumber = info.fecLineNumber;
// }
var newLine = info.videoCodecNumbersOriginal.split('SAVPF')[0] + 'SAVPF ';
var newOrder = [preferCodecNumber];
info.videoCodecNumbers.forEach(function(codecNumber) {
if (codecNumber === preferCodecNumber) return;
newOrder.push(codecNumber);
});
newLine += newOrder.join(' ');
sdp = sdp.replace(info.videoCodecNumbersOriginal, newLine);
return sdp;
}
function preferAudioCodecHelper(sdp, codec, info) {
var preferCodecNumber = '';
if (codec === 'opus') {
if (!info.opusLineNumber) {
return sdp;
}
preferCodecNumber = info.opusLineNumber;
} else if (codec === 'isac') {
if (!info.isacLineNumber) {
return sdp;
}
preferCodecNumber = info.isacLineNumber;
} else if (codec === 'g722') {
if (!info.g722LineNumber) {
return sdp;
}
preferCodecNumber = info.g722LineNumber;
} else if (codec === 'pcmu') {
if (!info.pcmuLineNumber) {
return sdp;
}
preferCodecNumber = info.pcmuLineNumber;
} else if (codec === 'pcma') {
if (!info.pcmaLineNumber) {
return sdp;
}
preferCodecNumber = info.pcmaLineNumber;
} else if (codec === 'red') {
if (!info.redLineNumber) {
return sdp;
}
preferCodecNumber = info.redLineNumber;
var preferCodecNumber = '';
var preferErrorCorrectionNumbers = [];
if (codec === 'vp8') {
preferCodecNumber = info.vp8LineNumber || '';
} else if (codec === 'vp9') {
preferCodecNumber = info.vp9LineNumber || '';
} else if (codec === 'h264') {
preferCodecNumber = info.h264LineNumber || '';
} else if (codec === 'h265') {
preferCodecNumber = info.h265LineNumber || '';
} else if (codec === 'av1') {
preferCodecNumber = info.av1LineNumber || '';
} else if (codec === 'red') { // you can treat red as a codec
preferCodecNumber = info.redLineNumber || '';
} else if (codec === 'fec') {
preferCodecNumber = info.ulpfecLineNumber || '';
}
var newLine = info.audioCodecNumbersOriginal.split('SAVPF')[0] + 'SAVPF ';
var newOrder = [preferCodecNumber];
info.audioCodecNumbers.forEach(function(codecNumber) {
if (codecNumber === preferCodecNumber) return;
newOrder.push(codecNumber);
});
newLine += newOrder.join(' ');
sdp = sdp.replace(info.audioCodecNumbersOriginal, newLine);
return sdp;
}
function splitLines(sdp) {
var info = {};
sdp.split('\n').forEach(function(line) {
if (line.indexOf('m=video') === 0) {
info.videoCodecNumbers = [];
line.split('SAVPF')[1].split(' ').forEach(function(codecNumber) {
codecNumber = codecNumber.trim();
if (!codecNumber || !codecNumber.length) return;
info.videoCodecNumbers.push(codecNumber);
info.videoCodecNumbersOriginal = line;
});
}
if (useRed && info.redLineNumber) { // or as a setting
preferErrorCorrectionNumbers.push(info.redLineNumber);
}
if (useUlpfec && info.ulpfecLineNumber) {
preferErrorCorrectionNumbers.push(info.ulpfecLineNumber);
}
if (preferCodecNumber === '') {
return sdp;
}
var newOrder = [preferCodecNumber].concat(preferErrorCorrectionNumbers);
info.videoCodecNumbers.forEach(function(codecNumber) {
if (!newOrder.includes(codecNumber)) {
newOrder.push(codecNumber);
}
});
var newLine = info.videoCodecNumbersOriginal.split('SAVPF')[0] + 'SAVPF ' + newOrder.join(' ');
sdp = sdp.replace(info.videoCodecNumbersOriginal, newLine);
return sdp;
}
function splitLines(sdp) {
var info = {};
sdp.split('\n').forEach(function(line) {
if (line.indexOf('m=video') === 0) {
info.videoCodecNumbers = [];
line.split('SAVPF')[1].split(' ').forEach(function(codecNumber) {
codecNumber = codecNumber.trim();
if (!codecNumber || !codecNumber.length) return;
info.videoCodecNumbers.push(codecNumber);
info.videoCodecNumbersOriginal = line;
});
}
var LINE = line.toUpperCase();
if (LINE.indexOf('VP8/90000') !== -1 && !info.vp8LineNumber) {
info.vp8LineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
}
if (LINE.indexOf('VP9/90000') !== -1 && !info.vp9LineNumber) {
info.vp9LineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
}
if (LINE.indexOf('H264/90000') !== -1 && !info.h264LineNumber) {
info.h264LineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
}
if (LINE.indexOf('VP8/90000') !== -1 && !info.vp8LineNumber) {
info.vp8LineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
}
if (LINE.indexOf('VP9/90000') !== -1 && !info.vp9LineNumber) {
info.vp9LineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
}
if (LINE.indexOf('H264/90000') !== -1 && !info.h264LineNumber) {
info.h264LineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
}
if (LINE.indexOf('H265/90000') !== -1 && !info.h265LineNumber) {
info.h265LineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
}
if (LINE.indexOf('AV1X/90000') !== -1 && !info.av1LineNumber) {
info.av1LineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
} else if (LINE.indexOf('AV1/90000') !== -1 && !info.av1LineNumber) {
info.av1LineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
}
//if (LINE.indexOf('RED/90000') !== -1 && !info.redLineNumber) {
// info.redLineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
// }
// if (LINE.indexOf('FEC/90000') !== -1 && !info.fecLineNumber) {
// info.fecLineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
// }
});
return info;
}
info.h265LineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
}
if (LINE.indexOf('AV1X/90000') !== -1 && !info.av1LineNumber) {
info.av1LineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
} else if (LINE.indexOf('AV1/90000') !== -1 && !info.av1LineNumber) {
info.av1LineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
}
if (LINE.indexOf('RED/90000') !== -1 && !info.redLineNumber) {
info.redLineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
}
if (LINE.indexOf('ULPFEC/90000') !== -1 && !info.ulpfecLineNumber) {
info.ulpfecLineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
}
});
return info;
}
function preferAudioCodec(sdp, codec, useRed=false, useUlpfec=false) {
if (codec) {
codec = codec.toLowerCase();
}
var info = splitAudioLines(sdp);
if (!info.audioCodecNumbers) {
return sdp;
}
var preferCodecNumber = '';
var errorCorrectionNumbers = [];
if (codec && info[codec + 'LineNumber']) {
preferCodecNumber = info[codec + 'LineNumber'];
}
if (useRed && info.redPcmLineNumber) {
errorCorrectionNumbers.push(info.redPcmLineNumber);
}
if (useRed && info.redLineNumber && !info.redPcmLineNumber) {
errorCorrectionNumbers.push(info.redLineNumber);
}
if (useUlpfec && info.ulpfecLineNumber) {
errorCorrectionNumbers.push(info.ulpfecLineNumber);
}
var newOrder = [preferCodecNumber].concat(errorCorrectionNumbers);
info.audioCodecNumbers.forEach(function(codecNumber) {
if (!newOrder.includes(codecNumber)) {
newOrder.push(codecNumber);
}
});
var newLine = info.audioCodecNumbersOriginal.split('SAVPF')[0] + 'SAVPF ' + newOrder.join(' ');
sdp = sdp.replace(info.audioCodecNumbersOriginal, newLine);
return sdp;
}
function splitAudioLines(sdp) {
var info = {};
@@ -254,37 +155,48 @@ var CodecsHandler = (function() {
info.audioCodecNumbersOriginal = line;
});
}
var LINE = line.toLowerCase();
if (LINE.indexOf('opus/48000') !== -1 && !info.opusLineNumber) {
info.opusLineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
}
if (LINE.indexOf('isac/32000') !== -1 && !info.isacLineNumber) {
info.isacLineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
}
if (LINE.indexOf('g722/8000') !== -1 && !info.g722LineNumber) {
info.g722LineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
}
if (LINE.indexOf('pcmu/8000') !== -1 && !info.pcmuLineNumber) {
info.pcmuLineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
}
if (LINE.indexOf('pcma/8000') !== -1 && !info.pcmaLineNumber) {
info.pcmaLineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
}
if (LINE.indexOf('red/48000') !== -1 && !info.redLineNumber) {
info.redLineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
}
if (LINE.indexOf('ulpfec/48000') !== -1 && !info.ulpfecLineNumber) {
info.ulpfecLineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
}
if (line.indexOf('red/8000') !== -1 && !info.redPcmLineNumber) {
info.redPcmLineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
}
if (line.indexOf('ulpfec/8000') !== -1 && !info.ulpfecLineNumber) {
info.ulpfecLineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
}
});
return info;
}
function addRedForPcmToSdp(sdp, info, redPcmLine) {
// Ensure RED for PCM is not already the first codec
if (!info.audioCodecNumbers.includes(redPcmLine)) {
var newOrder = info.audioCodecNumbers.filter(codecNumber => codecNumber !== redPcmLine);
newOrder.unshift(redPcmLine); // Add RED for PCM at the start
var newLine = info.audioCodecNumbersOriginal.split('SAVPF')[0] + 'SAVPF ' + newOrder.join(' ');
sdp = sdp.replace(info.audioCodecNumbersOriginal, newLine);
}
return sdp;
}
function extractSdp(sdpLine, pattern) {
var result = sdpLine.match(pattern);

View File

@@ -17,8 +17,7 @@ function streamSaverFunction(){
//console.log(ponyfill);
//console.log(isSecureContext);
// TODO: Must come up with a real detection test (#69)
let useBlobFallback = /constructor/i.test(global.HTMLElement) || !!global.safari || !!global.WebKitPoint;
let useBlobFallback = false; // we do not want to use blob recording because it can crash the browser.
//console.log(useBlobFallback);
@@ -214,7 +213,7 @@ function streamSaverFunction(){
WritableStream: global.WritableStream || ponyfill.WritableStream,
supported: true,
version: { full: '2.0.7', major: 2, minor: 0, dot: 7 },
mitm: './thirdparty/mitm.html?v=2'
mitm: './thirdparty/mitm.html?v=3'
}
//console.log(streamSaver);

317
thirdparty/StreamSaver_legacy.js vendored Normal file
View File

@@ -0,0 +1,317 @@
/*! streamsaver. MIT License. Jimmy Wärting <https://jimmy.warting.se/opensource> */
/* global chrome location ReadableStream define MessageChannel TransformStream */
function streamSaverFunction(){
'use strict'
const global = typeof window === 'object' ? window : this;
if (!global.HTMLElement) console.warn('streamsaver is meant to run on browsers main thread')
let mitmTransporter = null;
let supportsTransferable = false;
const test = fn => { try { fn() } catch (e) {} };
const ponyfill = global.WebStreamsPolyfill || {};
const isSecureContext = global.isSecureContext;
//console.log(ponyfill);
//console.log(isSecureContext);
// TODO: Must come up with a real detection test (#69)
let useBlobFallback = /constructor/i.test(global.HTMLElement) || !!global.safari || !!global.WebKitPoint;
//console.log(useBlobFallback);
const downloadStrategy = isSecureContext || 'MozAppearance' in document.documentElement.style
? 'iframe'
: 'navigate';
//console.log(downloadStrategy);
function createWriteStream (filename, stopStream){
//console.log("createWriteStream");
let opts = {
size: null,
pathname: null,
writableStrategy: undefined,
readableStrategy: undefined
}
let bytesWritten = 0 // by StreamSaver.js (not the service worker)
let downloadUrl = null
let channel = null
let ts = null
if (!useBlobFallback) {
loadTransporter()
channel = new MessageChannel()
// Make filename RFC5987 compatible
filename = encodeURIComponent(filename.replace(/\//g, ':'))
.replace(/['()]/g, escape)
.replace(/\*/g, '%2A')
const response = {
transferringReadable: supportsTransferable,
pathname: opts.pathname || Math.random().toString().slice(-6) + '/' + filename,
headers: {
'Content-Type': 'application/octet-stream; charset=utf-8',
'Content-Disposition': "attachment; filename*=UTF-8''" + filename
}
}
if (opts.size) {
response.headers['Content-Length'] = opts.size
}
const args = [ response, '*', [ channel.port2 ] ]
if (supportsTransferable) {
const transformer = downloadStrategy === 'iframe' ? undefined : {
// This transformer & flush method is only used by insecure context.
transform (chunk, controller) {
if (!(chunk instanceof Uint8Array)) {
throw new TypeError('Can only write Uint8Arrays')
}
bytesWritten += chunk.length
controller.enqueue(chunk)
if (downloadUrl) {
location.href = downloadUrl
downloadUrl = null
}
},
flush () {
if (downloadUrl) {
location.href = downloadUrl
}
}
}
ts = new streamSaver.TransformStream(
transformer,
opts.writableStrategy,
opts.readableStrategy
)
const readableStream = ts.readable
channel.port1.postMessage({ readableStream }, [ readableStream ])
}
channel.port1.onmessage = evt => {
console.log(evt);
// Service worker sent us a link that we should open.
if (evt.data.download) {
// Special treatment for popup...
if (downloadStrategy === 'navigate') {
mitmTransporter.remove()
mitmTransporter = null
if (bytesWritten) {
location.href = evt.data.download
} else {
downloadUrl = evt.data.download
}
} else {
if (mitmTransporter.isPopup) {
mitmTransporter.remove()
mitmTransporter = null
// Special case for firefox, they can keep sw alive with fetch
if (downloadStrategy === 'iframe') {
makeIframe(streamSaver.mitm)
}
}
// We never remove this iframes b/c it can interrupt saving
makeIframe(evt.data.download)
}
} else if (evt.data.abort) {
stopStream(false, true);
chunks = []
channel.port1.postMessage('abort') //send back so controller is aborted
channel.port1.onmessage = null
setTimeout(function(channel){
channel.port1.close()
channel.port2.close()
channel = null
},1300,channel);
}
}
if (mitmTransporter.loaded) {
mitmTransporter.postMessage(...args)
} else {
mitmTransporter.addEventListener('load', () => {
mitmTransporter.postMessage(...args)
}, { once: true })
}
}
let chunks = []
return (!useBlobFallback && ts && ts.writable) || new streamSaver.WritableStream({
write (chunk) {
if (!(chunk instanceof Uint8Array)) {
throw new TypeError('Can only write Uint8Arrays')
}
if (useBlobFallback) {
// Safari... The new IE6
// https://github.com/jimmywarting/StreamSaver.js/issues/69
//
// even though it has everything it fails to download anything
// that comes from the service worker..!
chunks.push(chunk)
return
}
// is called when a new chunk of data is ready to be written
// to the underlying sink. It can return a promise to signal
// success or failure of the write operation. The stream
// implementation guarantees that this method will be called
// only after previous writes have succeeded, and never after
// close or abort is called.
// TODO: Kind of important that service worker respond back when
// it has been written. Otherwise we can't handle backpressure
// EDIT: Transferable streams solves this...
try {
channel.port1.postMessage(chunk)
} catch(e){
};
bytesWritten += chunk.length
if (downloadUrl) {
location.href = downloadUrl
downloadUrl = null
}
},
close () {
if (useBlobFallback) {
const blob = new Blob(chunks, { type: 'application/octet-stream; charset=utf-8' })
const link = document.createElement('a')
link.href = URL.createObjectURL(blob)
link.download = filename
link.click()
} else {
channel.port1.postMessage('end')
}
},
abort () {
chunks = []
channel.port1.postMessage('abort')
channel.port1.onmessage = null
setTimeout(function(channel){
channel.port1.close()
channel.port2.close()
channel = null
},1300,channel);
}
}, opts.writableStrategy)
}
const streamSaver = {
createWriteStream,
WritableStream: global.WritableStream || ponyfill.WritableStream,
supported: true,
version: { full: '2.0.7', major: 2, minor: 0, dot: 7 },
mitm: './thirdparty/mitm.html?v=2'
}
//console.log(streamSaver);
/**
* create a hidden iframe and append it to the DOM (body)
*
* @param {string} src page to load
* @return {HTMLIFrameElement} page to load
*/
function makeIframe (src) {
if (!src) throw new Error('meh')
const iframe = document.createElement('iframe')
iframe.hidden = true
iframe.src = src
iframe.loaded = false
iframe.name = 'iframe'
iframe.isIframe = true
iframe.postMessage = (...args) => iframe.contentWindow.postMessage(...args)
iframe.addEventListener('load', () => {
iframe.loaded = true
}, { once: true })
document.body.appendChild(iframe)
return iframe
}
/**
* create a popup that simulates the basic things
* of what a iframe can do
*
* @param {string} src page to load
* @return {object} iframe like object
*/
function makePopup (src) {
const options = 'width=200,height=100'
const delegate = document.createDocumentFragment()
const popup = {
frame: global.open(src, 'popup', options),
loaded: false,
isIframe: false,
isPopup: true,
remove () { popup.frame.close() },
addEventListener (...args) { delegate.addEventListener(...args) },
dispatchEvent (...args) { delegate.dispatchEvent(...args) },
removeEventListener (...args) { delegate.removeEventListener(...args) },
postMessage (...args) { popup.frame.postMessage(...args) }
}
const onReady = evt => {
if (evt.source === popup.frame) {
popup.loaded = true
global.removeEventListener('message', onReady)
popup.dispatchEvent(new Event('load'))
}
}
global.addEventListener('message', onReady)
return popup
}
try {
// We can't look for service worker since it may still work on http
new Response(new ReadableStream())
if (isSecureContext && !('serviceWorker' in navigator)) {
useBlobFallback = true
}
} catch (err) {
useBlobFallback = true
}
//console.log("useBlobFallback: "+useBlobFallback);
test(() => {
// Transferable stream was first enabled in chrome v73 behind a flag
const { readable } = new TransformStream()
const mc = new MessageChannel()
mc.port1.postMessage(readable, [readable])
mc.port1.close()
mc.port2.close()
supportsTransferable = true
// Freeze TransformStream object (can only work with native)
Object.defineProperty(streamSaver, 'TransformStream', {
configurable: false,
writable: false,
value: TransformStream
})
})
function loadTransporter () {
if (!mitmTransporter) {
mitmTransporter = isSecureContext
? makeIframe(streamSaver.mitm)
: makePopup(streamSaver.mitm)
}
}
return streamSaver
};
var streamSaver = streamSaverFunction();