snapshot of https://github.com/jitsi/jitsi-meet.git @ fd1749dd28f4138aa80234fc7f311b19aaffa0e7 (2026-09-15)
This commit is contained in:
119
react/features/pip/middleware.ts
Normal file
119
react/features/pip/middleware.ts
Normal 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;
|
||||
});
|
||||
Reference in New Issue
Block a user