Rewrite volume control feature

This commit is contained in:
redphx 2024-05-05 09:36:53 +07:00
parent 23fb50cb6f
commit e852b246d3
3 changed files with 60 additions and 38 deletions

View File

@ -459,6 +459,29 @@ BxLogger.info('patchRemotePlayMkb', ${configsVar});
return str; return str;
}, },
patchAudioMediaStream(str: string) {
const text = '.srcObject=this.audioMediaStream,';
if (!str.includes(text)) {
return false;
}
const newCode = `window.BX_EXPOSED.setupGainNode(arguments[1], this.audioMediaStream),`;
str = str.replace(text, text + newCode);
return str;
},
patchCombinedAudioVideoMediaStream(str: string) {
const text = '.srcObject=this.combinedAudioVideoStream';
if (!str.includes(text)) {
return false;
}
const newCode = `,window.BX_EXPOSED.setupGainNode(arguments[0], this.combinedAudioVideoStream)`;
str = str.replace(text, text + newCode);
return str;
},
}; };
let PATCH_ORDERS: PatchArray = [ let PATCH_ORDERS: PatchArray = [
@ -501,6 +524,12 @@ let PLAYING_PATCH_ORDERS: PatchArray = [
'patchStreamHud', 'patchStreamHud',
'playVibration', 'playVibration',
// Patch volume control for normal stream
getPref(PrefKey.AUDIO_ENABLE_VOLUME_CONTROL) && !getPref(PrefKey.STREAM_COMBINE_SOURCES) && 'patchAudioMediaStream',
// Patch volume control for combined audio+video stream
getPref(PrefKey.AUDIO_ENABLE_VOLUME_CONTROL) && getPref(PrefKey.STREAM_COMBINE_SOURCES) && 'patchCombinedAudioVideoMediaStream',
STATES.hasTouchSupport && getPref(PrefKey.STREAM_TOUCH_CONTROLLER) === 'all' && 'exposeTouchLayoutManager', STATES.hasTouchSupport && getPref(PrefKey.STREAM_TOUCH_CONTROLLER) === 'all' && 'exposeTouchLayoutManager',
STATES.hasTouchSupport && (getPref(PrefKey.STREAM_TOUCH_CONTROLLER) === 'off' || getPref(PrefKey.STREAM_TOUCH_CONTROLLER_AUTO_OFF)) && 'disableTakRenderer', STATES.hasTouchSupport && (getPref(PrefKey.STREAM_TOUCH_CONTROLLER) === 'off' || getPref(PrefKey.STREAM_TOUCH_CONTROLLER_AUTO_OFF)) && 'disableTakRenderer',

View File

@ -91,5 +91,26 @@ export const BxExposed = {
BxEvent.dispatch(window, BxEvent.TITLE_INFO_READY); BxEvent.dispatch(window, BxEvent.TITLE_INFO_READY);
return titleInfo; return titleInfo;
},
setupGainNode: ($media: HTMLMediaElement, audioStream: MediaStream) => {
if ($media instanceof HTMLAudioElement) {
$media.muted = true;
$media.addEventListener('playing', e => {
$media.muted = true;
$media.pause();
});
} else {
$media.muted = true;
$media.addEventListener('playing', e => {
$media.muted = true;
});
}
const audioCtx = STATES.currentStream.audioContext!;
const source = audioCtx.createMediaStreamSource(audioStream);
const gainNode = audioCtx.createGain(); // call monkey-patched createGain() in BxAudioContext
source.connect(gainNode).connect(audioCtx.destination);
} }
}; };

View File

@ -1,7 +1,6 @@
import { BxEvent } from "@utils/bx-event"; import { BxEvent } from "@utils/bx-event";
import { getPref, PrefKey } from "@utils/preferences"; import { getPref, PrefKey } from "@utils/preferences";
import { STATES } from "@utils/global"; import { STATES } from "@utils/global";
import { UserAgent } from "@utils/user-agent";
import { BxLogger } from "@utils/bx-logger"; import { BxLogger } from "@utils/bx-logger";
export function patchVideoApi() { export function patchVideoApi() {
@ -104,10 +103,6 @@ export function patchRtcPeerConnection() {
STATES.currentStream.peerConnection = conn; STATES.currentStream.peerConnection = conn;
conn.addEventListener('connectionstatechange', e => { conn.addEventListener('connectionstatechange', e => {
if (conn.connectionState === 'connecting') {
STATES.currentStream.audioGainNode = null;
}
BxLogger.info('connectionstatechange', conn.connectionState); BxLogger.info('connectionstatechange', conn.connectionState);
}); });
return conn; return conn;
@ -115,46 +110,23 @@ export function patchRtcPeerConnection() {
} }
export function patchAudioContext() { export function patchAudioContext() {
if (UserAgent.isSafari(true)) { const OrgAudioContext = window.AudioContext;
const nativeCreateGain = window.AudioContext.prototype.createGain; const nativeCreateGain = OrgAudioContext.prototype.createGain;
window.AudioContext.prototype.createGain = function() {
// @ts-ignore
window.AudioContext = function(options?: AudioContextOptions | undefined): AudioContext {
const ctx = new OrgAudioContext(options);
BxLogger.info('patchAudioContext', ctx, options);
ctx.createGain = function() {
const gainNode = nativeCreateGain.apply(this); const gainNode = nativeCreateGain.apply(this);
gainNode.gain.value = getPref(PrefKey.AUDIO_VOLUME) / 100; gainNode.gain.value = getPref(PrefKey.AUDIO_VOLUME) / 100;
STATES.currentStream.audioGainNode = gainNode; STATES.currentStream.audioGainNode = gainNode;
return gainNode; return gainNode;
} }
}
const OrgAudioContext = window.AudioContext;
// @ts-ignore
window.AudioContext = function() {
const ctx = new OrgAudioContext();
STATES.currentStream.audioContext = ctx; STATES.currentStream.audioContext = ctx;
STATES.currentStream.audioGainNode = null;
return ctx; return ctx;
} }
const nativePlay = HTMLAudioElement.prototype.play;
HTMLAudioElement.prototype.play = function() {
this.muted = true;
const promise = nativePlay.apply(this);
if (STATES.currentStream.audioGainNode) {
return promise;
}
this.addEventListener('playing', e => (e.target as HTMLAudioElement).pause());
const audioCtx = STATES.currentStream.audioContext!;
// TOOD: check srcObject
const audioStream = audioCtx.createMediaStreamSource(this.srcObject as any);
const gainNode = audioCtx.createGain();
audioStream.connect(gainNode);
gainNode.connect(audioCtx.destination);
gainNode.gain.value = getPref(PrefKey.AUDIO_VOLUME) / 100;
STATES.currentStream.audioGainNode = gainNode;
return promise;
}
} }