snapshot of https://github.com/jitsi/jitsi-meet.git @ fd1749dd28f4138aa80234fc7f311b19aaffa0e7 (2026-09-15)
Some checks failed
Prosody Plugin Tests / test (push) Failing after 10m11s
Close stale issues and PRs / stale (push) Failing after 1s

This commit is contained in:
upstream-mirror
2026-09-15 15:56:32 +05:00
commit 1d2bfcac0c
3159 changed files with 464524 additions and 0 deletions

View File

@@ -0,0 +1,119 @@
import { AnyAction } from 'redux';
import { IReduxState, IStore } from '../app/types';
import { CONFERENCE_FAILED, CONFERENCE_LEFT, CONFERENCE_WILL_LEAVE } from '../base/conference/actionTypes';
import { browser } from '../base/lib-jitsi-meet';
import MediaCastSender from '../base/media-cast/MediaCastSender';
import type { MediaCastSignal } from '../base/media-cast/types.web';
import MiddlewareRegistry from '../base/redux/MiddlewareRegistry';
import StateListenerRegistry from '../base/redux/StateListenerRegistry';
import { isEmbedded } from '../base/util/embedUtils';
import { getLargeVideoParticipant } from '../large-video/functions';
import {
HOST_DOCUMENT_PIP_CLOSED,
HOST_DOCUMENT_PIP_OPENED,
HOST_DOCUMENT_PIP_SIGNAL_RECEIVED
} from './actionTypes';
import { clearHostDocumentPiPPendingState, exitPiP } from './actions';
import { getPiPVideoTrack, isDocumentPiPRequestPending, setDocumentPiPRequestPending } from './functions';
import logger from './logger';
import './subscriber';
/**
* Sender associated with the currently open host-owned Document PiP window.
*/
let mediaCastSender: MediaCastSender | undefined;
/**
* Returns the live native video track selected for PiP without taking ownership of it.
*
* @param {IReduxState} state - Current Redux state.
* @returns {MediaStreamTrack|null}
*/
function getNativePiPTrack(state: IReduxState): MediaStreamTrack | null {
const videoTrack = getPiPVideoTrack(state, getLargeVideoParticipant(state));
const nativeTrack = videoTrack?.jitsiTrack?.getTrack();
return videoTrack?.muted || nativeTrack?.readyState !== 'live' ? null : nativeTrack;
}
/**
* Stops and releases the media-cast sender without stopping its conference-owned source track.
*
* @returns {void}
*/
function stopSender() {
mediaCastSender?.stop();
mediaCastSender = undefined;
}
/**
* Starts the media-cast sender or updates its current source track.
*
* @param {IStore} store - Redux store used to select the initial track.
* @returns {void}
*/
function startSender(store: IStore) {
if (mediaCastSender) {
void mediaCastSender.setTrack(getNativePiPTrack(store.getState()));
return;
}
mediaCastSender = new MediaCastSender({
onError: error => logger.error('Embedded Document PiP media sender failed:', error),
onSignal: signal => APP.API.notifyDocumentPiPSignal(signal)
});
mediaCastSender.start(getNativePiPTrack(store.getState()));
}
StateListenerRegistry.register(
/* selector */ (state: IReduxState) => {
const pipState = state['features/pip'];
return isEmbedded() && pipState?.isPiPActive
? getNativePiPTrack(state)
: undefined;
},
/* listener */ (track: MediaStreamTrack | null | undefined) => {
if (track !== undefined) {
mediaCastSender?.setTrack(track)
.catch(error => logger.error('Failed to update embedded Document PiP track:', error));
}
}
);
MiddlewareRegistry.register((store: IStore) => (next: Function) => (action: AnyAction) => {
const result = next(action);
switch (action.type) {
case HOST_DOCUMENT_PIP_OPENED:
// Electron stays on Video PiP and never has a host receiver, so no sender is started.
if (!browser.isElectron()) {
startSender(store);
}
break;
case HOST_DOCUMENT_PIP_CLOSED:
stopSender();
break;
case HOST_DOCUMENT_PIP_SIGNAL_RECEIVED:
mediaCastSender?.handleSignal(action.signal as MediaCastSignal);
break;
case CONFERENCE_WILL_LEAVE:
case CONFERENCE_FAILED:
case CONFERENCE_LEFT:
clearHostDocumentPiPPendingState();
if (store.getState()['features/pip']?.isPiPActive) {
store.dispatch(exitPiP());
} else if (isDocumentPiPRequestPending()) {
setDocumentPiPRequestPending(false);
APP.API.notifyDocumentPiPClose();
}
stopSender();
break;
}
return result;
});