Remove each user stream individually
This commit is contained in:
parent
f056048d62
commit
46a0b1f7ea
@ -68,9 +68,20 @@ class PeerHandler {
|
||||
}
|
||||
handleTrack = (track: MediaStreamTrack, stream: MediaStream) => {
|
||||
const { user, dispatch } = this
|
||||
debug('peer: %s, track', user.id)
|
||||
const userId = user.id
|
||||
debug('peer: %s, track', userId)
|
||||
// Listen to mute event to know when a track was removed
|
||||
// https://github.com/feross/simple-peer/issues/512
|
||||
track.onmute = () => {
|
||||
debug('peer: %s, track muted', userId)
|
||||
dispatch(StreamActions.removeTrack({
|
||||
userId,
|
||||
stream,
|
||||
track,
|
||||
}))
|
||||
}
|
||||
dispatch(StreamActions.addStream({
|
||||
userId: user.id,
|
||||
userId,
|
||||
stream,
|
||||
}))
|
||||
}
|
||||
@ -97,10 +108,13 @@ class PeerHandler {
|
||||
}
|
||||
}
|
||||
handleClose = () => {
|
||||
const { dispatch, user } = this
|
||||
debug('peer: %s, close', user.id)
|
||||
const { dispatch, user, getState } = this
|
||||
dispatch(NotifyActions.error('Peer connection closed'))
|
||||
dispatch(StreamActions.removeStream(user.id))
|
||||
const state = getState()
|
||||
const userStreams = state.streams[user.id]
|
||||
userStreams && userStreams.streams.forEach(s => {
|
||||
dispatch(StreamActions.removeStream(user.id, s.stream))
|
||||
})
|
||||
dispatch(removePeer(user.id))
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,12 +20,16 @@ export interface RemoveStreamAction {
|
||||
|
||||
export interface RemoveStreamPayload {
|
||||
userId: string
|
||||
stream?: MediaStream
|
||||
stream: MediaStream
|
||||
}
|
||||
|
||||
export interface SetActiveStreamPayload {
|
||||
userId: string
|
||||
}
|
||||
|
||||
export interface SetActiveStreamAction {
|
||||
type: 'ACTIVE_SET'
|
||||
payload: RemoveStreamPayload
|
||||
payload: SetActiveStreamPayload
|
||||
}
|
||||
|
||||
export interface ToggleActiveStreamAction {
|
||||
@ -33,6 +37,17 @@ export interface ToggleActiveStreamAction {
|
||||
payload: UserIdPayload
|
||||
}
|
||||
|
||||
export interface RemoveStreamTrackPayload {
|
||||
userId: string
|
||||
stream: MediaStream
|
||||
track: MediaStreamTrack
|
||||
}
|
||||
|
||||
export interface RemoveStreamTrackAction {
|
||||
type: 'PEER_STREAM_TRACK_REMOVE'
|
||||
payload: RemoveStreamTrackPayload
|
||||
}
|
||||
|
||||
export interface UserIdPayload {
|
||||
userId: string
|
||||
}
|
||||
@ -44,12 +59,19 @@ export const addStream = (payload: AddStreamPayload): AddStreamAction => ({
|
||||
|
||||
export const removeStream = (
|
||||
userId: string,
|
||||
stream?: MediaStream,
|
||||
stream: MediaStream,
|
||||
): RemoveStreamAction => ({
|
||||
type: constants.STREAM_REMOVE,
|
||||
payload: { userId, stream },
|
||||
})
|
||||
|
||||
export const removeTrack = (
|
||||
payload: RemoveStreamTrackPayload,
|
||||
): RemoveStreamTrackAction => ({
|
||||
type: constants.STREAM_TRACK_REMOVE,
|
||||
payload,
|
||||
})
|
||||
|
||||
export const setActive = (userId: string): SetActiveStreamAction => ({
|
||||
type: constants.ACTIVE_SET,
|
||||
payload: { userId },
|
||||
@ -64,4 +86,5 @@ export type StreamAction =
|
||||
AddStreamAction |
|
||||
RemoveStreamAction |
|
||||
SetActiveStreamAction |
|
||||
ToggleActiveStreamAction
|
||||
ToggleActiveStreamAction |
|
||||
RemoveStreamTrackAction
|
||||
|
||||
@ -63,7 +63,16 @@ export default class App extends React.PureComponent<AppProps, AppState> {
|
||||
init()
|
||||
}
|
||||
onHangup = () => {
|
||||
this.props.removeStream(constants.ME)
|
||||
const localStreams = this.getLocalStreams()
|
||||
localStreams.streams.forEach(s => {
|
||||
this.props.removeStream(constants.ME, s.stream)
|
||||
})
|
||||
}
|
||||
getLocalStreams() {
|
||||
return this.props.streams[constants.ME] || {
|
||||
userId: constants.ME,
|
||||
streams: [],
|
||||
}
|
||||
}
|
||||
render () {
|
||||
const {
|
||||
@ -86,10 +95,7 @@ export default class App extends React.PureComponent<AppProps, AppState> {
|
||||
'chat-visible': this.state.chatVisible,
|
||||
})
|
||||
|
||||
const localStreams = streams[constants.ME] || {
|
||||
userId: constants.ME,
|
||||
streams: [],
|
||||
}
|
||||
const localStreams = this.getLocalStreams()
|
||||
|
||||
return (
|
||||
<div className="app">
|
||||
|
||||
@ -24,7 +24,6 @@ function mapStateToProps(state: State) {
|
||||
const hidden = !!localStream &&
|
||||
localStream.streams.filter(s => s.type === STREAM_TYPE_CAMERA).length > 0
|
||||
const visible = !hidden
|
||||
console.log('visible', visible)
|
||||
return {
|
||||
...state.media,
|
||||
visible,
|
||||
|
||||
@ -37,6 +37,7 @@ export const SOCKET_EVENT_USERS = 'users'
|
||||
|
||||
export const STREAM_ADD = 'PEER_STREAM_ADD'
|
||||
export const STREAM_REMOVE = 'PEER_STREAM_REMOVE'
|
||||
export const STREAM_TRACK_REMOVE = 'PEER_STREAM_TRACK_REMOVE'
|
||||
|
||||
export const STREAM_TYPE_CAMERA = 'camera'
|
||||
export const STREAM_TYPE_DESKTOP = 'desktop'
|
||||
|
||||
@ -4,6 +4,7 @@ import Peer from 'simple-peer'
|
||||
import { PeerAction } from '../actions/PeerActions'
|
||||
import * as constants from '../constants'
|
||||
import { MediaStreamAction } from '../actions/MediaActions'
|
||||
import { RemoveStreamAction } from '../actions/StreamActions'
|
||||
|
||||
export type PeersState = Record<string, Peer.Instance>
|
||||
|
||||
@ -11,9 +12,26 @@ const defaultState: PeersState = {}
|
||||
|
||||
let localStreams: Record<string, MediaStream> = {}
|
||||
|
||||
function handleRemoveStream(
|
||||
state: PeersState,
|
||||
action: RemoveStreamAction,
|
||||
): PeersState {
|
||||
const stream = action.payload.stream
|
||||
if (action.payload.userId === constants.ME) {
|
||||
forEach(state, peer => {
|
||||
console.log('removing track from peer')
|
||||
stream.getTracks().forEach(track => {
|
||||
peer.removeTrack(track, stream)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return state
|
||||
}
|
||||
|
||||
export default function peers(
|
||||
state = defaultState,
|
||||
action: PeerAction | MediaStreamAction,
|
||||
action: PeerAction | MediaStreamAction | RemoveStreamAction,
|
||||
): PeersState {
|
||||
switch (action.type) {
|
||||
case constants.PEER_ADD:
|
||||
@ -27,6 +45,8 @@ export default function peers(
|
||||
localStreams = {}
|
||||
forEach(state, peer => peer.destroy())
|
||||
return defaultState
|
||||
case constants.STREAM_REMOVE:
|
||||
return handleRemoveStream(state, action)
|
||||
case constants.MEDIA_STREAM:
|
||||
if (action.status === 'resolved') {
|
||||
forEach(state, peer => {
|
||||
|
||||
@ -57,11 +57,11 @@ describe('reducers/alerts', () => {
|
||||
describe('removeStream', () => {
|
||||
it('removes a stream', () => {
|
||||
store.dispatch(StreamActions.addStream({ userId, stream }))
|
||||
store.dispatch(StreamActions.removeStream(userId))
|
||||
store.dispatch(StreamActions.removeStream(userId, stream))
|
||||
expect(store.getState().streams).toEqual({})
|
||||
})
|
||||
it('does not fail when no stream', () => {
|
||||
store.dispatch(StreamActions.removeStream(userId))
|
||||
store.dispatch(StreamActions.removeStream(userId, stream))
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import _debug from 'debug'
|
||||
import omit from 'lodash/omit'
|
||||
import { AddStreamAction, RemoveStreamAction, StreamAction, StreamType } from '../actions/StreamActions'
|
||||
import { STREAM_ADD, STREAM_REMOVE, MEDIA_STREAM } from '../constants'
|
||||
import { AddStreamAction, RemoveStreamAction, StreamAction, StreamType, RemoveStreamTrackAction } from '../actions/StreamActions'
|
||||
import { STREAM_ADD, STREAM_REMOVE, MEDIA_STREAM, STREAM_TRACK_REMOVE } from '../constants'
|
||||
import { createObjectURL, revokeObjectURL } from '../window'
|
||||
import { MediaStreamAction } from '../actions/MediaActions'
|
||||
|
||||
@ -99,6 +99,27 @@ function removeStream (
|
||||
return omit(state, [userId])
|
||||
}
|
||||
|
||||
function removeStreamTrack(
|
||||
state: StreamsState, payload: RemoveStreamTrackAction['payload'],
|
||||
): StreamsState {
|
||||
const { userId, stream, track } = payload
|
||||
const userStreams = state[userId]
|
||||
if (!userStreams) {
|
||||
return state
|
||||
}
|
||||
const index = userStreams.streams.map(s => s.stream).indexOf(stream)
|
||||
if (index < 0) {
|
||||
return state
|
||||
}
|
||||
stream.removeTrack(track)
|
||||
if (stream.getTracks().length === 0) {
|
||||
return removeStream(state, {userId, stream})
|
||||
}
|
||||
// UI does not update when a stream track is removed so there is no need to
|
||||
// update the state object
|
||||
return state
|
||||
}
|
||||
|
||||
export default function streams(
|
||||
state = defaultState,
|
||||
action: StreamAction | MediaStreamAction,
|
||||
@ -108,6 +129,8 @@ export default function streams(
|
||||
return addStream(state, action.payload)
|
||||
case STREAM_REMOVE:
|
||||
return removeStream(state, action.payload)
|
||||
case STREAM_TRACK_REMOVE:
|
||||
return removeStreamTrack(state, action.payload)
|
||||
case MEDIA_STREAM:
|
||||
if (action.status === 'resolved') {
|
||||
return addStream(state, action.payload)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user