diff --git a/src/client/__mocks__/window.ts b/src/client/__mocks__/window.ts index cbe49b4..a17aaaa 100644 --- a/src/client/__mocks__/window.ts +++ b/src/client/__mocks__/window.ts @@ -32,6 +32,9 @@ window.navigator.mediaDevices.enumerateDevices = async () => { window.navigator.mediaDevices.getUserMedia = async () => { return {} as any } +(window.navigator.mediaDevices as any).getDisplayMedia = async () => { + return {} as any +} export const play = jest.fn() diff --git a/src/client/actions/MediaActions.ts b/src/client/actions/MediaActions.ts index 39002e4..8ef4410 100644 --- a/src/client/actions/MediaActions.ts +++ b/src/client/actions/MediaActions.ts @@ -1,5 +1,5 @@ import { makeAction, AsyncAction } from '../async' -import { MEDIA_AUDIO_CONSTRAINT_SET, MEDIA_VIDEO_CONSTRAINT_SET, MEDIA_ENUMERATE, MEDIA_STREAM } from '../constants' +import { MEDIA_AUDIO_CONSTRAINT_SET, MEDIA_VIDEO_CONSTRAINT_SET, MEDIA_ENUMERATE, MEDIA_STREAM, ME, ME_DESKTOP } from '../constants' import _debug from 'debug' const debug = _debug('peercalls') @@ -66,6 +66,11 @@ async function getUserMedia( }) } +async function getDisplayMedia(): Promise { + const mediaDevices = navigator.mediaDevices as any // eslint-disable-line + return mediaDevices.getDisplayMedia({video: true, audio: false}) +} + export interface MediaVideoConstraintAction { type: 'MEDIA_VIDEO_CONSTRAINT_SET' payload: VideoConstraint @@ -106,12 +111,33 @@ export const getMediaStream = makeAction( MEDIA_STREAM, async (constraints: GetMediaConstraints) => { debug('getMediaStream', constraints) - return getUserMedia(constraints) + const payload: MediaStreamPayload = { + stream: await getUserMedia(constraints), + userId: ME, + } + return payload }, ) +export const getDesktopStream = makeAction( + MEDIA_STREAM, + async () => { + debug('getDesktopStream') + const payload: MediaStreamPayload = { + stream: await getDisplayMedia(), + userId: ME_DESKTOP, + } + return payload + }, +) + +export interface MediaStreamPayload { + stream: MediaStream + userId: string +} + export type MediaEnumerateAction = AsyncAction<'MEDIA_ENUMERATE', MediaDevice[]> -export type MediaStreamAction = AsyncAction<'MEDIA_STREAM', MediaStream> +export type MediaStreamAction = AsyncAction<'MEDIA_STREAM', MediaStreamPayload> export type MediaPlayAction = AsyncAction<'MEDIA_PLAY', void> export type MediaAction = diff --git a/src/client/actions/PeerActions.ts b/src/client/actions/PeerActions.ts index c0ff94d..6004f8a 100644 --- a/src/client/actions/PeerActions.ts +++ b/src/client/actions/PeerActions.ts @@ -63,6 +63,10 @@ class PeerHandler { // we no longer automatically send the stream to the peer. peer.addStream(localStream.stream) } + const desktopStream = state.streams[constants.ME_DESKTOP] + if (desktopStream && desktopStream.stream) { + peer.addStream(desktopStream.stream) + } } handleStream = (stream: MediaStream) => { const { user, dispatch } = this diff --git a/src/client/components/App.tsx b/src/client/components/App.tsx index 630d1f5..305da81 100644 --- a/src/client/components/App.tsx +++ b/src/client/components/App.tsx @@ -13,6 +13,7 @@ import Notifications from './Notifications' import { Side } from './Side' import Toolbar from './Toolbar' import Video from './Video' +import { getDesktopStream } from '../actions/MediaActions' export interface AppProps { active: string | null @@ -25,6 +26,7 @@ export interface AppProps { play: () => void sendMessage: (message: TextMessage) => void streams: Record + getDesktopStream: typeof getDesktopStream removeStream: typeof removeStream onSendFile: (file: File) => void toggleActive: (userId: string) => void @@ -35,6 +37,8 @@ export interface AppState { chatVisible: boolean } +const localStreams: string[] = [constants.ME, constants.ME_DESKTOP] + export default class App extends React.PureComponent { state: AppState = { videos: {}, @@ -61,6 +65,7 @@ export default class App extends React.PureComponent { } onHangup = () => { this.props.removeStream(constants.ME) + this.props.removeStream(constants.ME_DESKTOP) } render () { const { @@ -93,6 +98,9 @@ export default class App extends React.PureComponent { onSendFile={onSendFile} onHangup={this.onHangup} stream={streams[constants.ME]} + desktopStream={streams[constants.ME_DESKTOP]} + onGetDesktopStream={this.props.getDesktopStream} + onRemoveStream={this.props.removeStream} /> @@ -109,19 +117,19 @@ export default class App extends React.PureComponent { visible={this.state.chatVisible} />
- {streams[constants.ME] && ( + {localStreams.map(userId => (