Merge pull request #652 from vector-im/dbkr/fixec_screenshare
Fix screen sharing
This commit is contained in:
		
				commit
				
					
						b39b3c072d
					
				
			
		
					 4 changed files with 98 additions and 29 deletions
				
			
		| 
						 | 
					@ -44,7 +44,7 @@
 | 
				
			||||||
    "i18next": "^21.10.0",
 | 
					    "i18next": "^21.10.0",
 | 
				
			||||||
    "i18next-browser-languagedetector": "^6.1.8",
 | 
					    "i18next-browser-languagedetector": "^6.1.8",
 | 
				
			||||||
    "i18next-http-backend": "^1.4.4",
 | 
					    "i18next-http-backend": "^1.4.4",
 | 
				
			||||||
    "matrix-js-sdk": "github:matrix-org/matrix-js-sdk#5a0787349d4951012eabe72f3363c17bdcda0d56",
 | 
					    "matrix-js-sdk": "github:matrix-org/matrix-js-sdk#1666419beaf08cafefdc34cced417cb0ba120b2e",
 | 
				
			||||||
    "matrix-widget-api": "^1.0.0",
 | 
					    "matrix-widget-api": "^1.0.0",
 | 
				
			||||||
    "mermaid": "^8.13.8",
 | 
					    "mermaid": "^8.13.8",
 | 
				
			||||||
    "normalize.css": "^8.0.1",
 | 
					    "normalize.css": "^8.0.1",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -27,10 +27,11 @@ import { MatrixCall } from "matrix-js-sdk/src/webrtc/call";
 | 
				
			||||||
import { CallFeed } from "matrix-js-sdk/src/webrtc/callFeed";
 | 
					import { CallFeed } from "matrix-js-sdk/src/webrtc/callFeed";
 | 
				
			||||||
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
 | 
					import { RoomMember } from "matrix-js-sdk/src/models/room-member";
 | 
				
			||||||
import { useTranslation } from "react-i18next";
 | 
					import { useTranslation } from "react-i18next";
 | 
				
			||||||
 | 
					import { IWidgetApiRequest } from "matrix-widget-api";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { usePageUnload } from "./usePageUnload";
 | 
					import { usePageUnload } from "./usePageUnload";
 | 
				
			||||||
import { TranslatedError, translatedError } from "../TranslatedError";
 | 
					import { TranslatedError, translatedError } from "../TranslatedError";
 | 
				
			||||||
import { ElementWidgetActions, widget } from "../widget";
 | 
					import { ElementWidgetActions, ScreenshareStartData, widget } from "../widget";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface UseGroupCallReturnType {
 | 
					export interface UseGroupCallReturnType {
 | 
				
			||||||
  state: GroupCallState;
 | 
					  state: GroupCallState;
 | 
				
			||||||
| 
						 | 
					@ -302,36 +303,84 @@ export function useGroupCall(groupCall: GroupCall): UseGroupCallReturnType {
 | 
				
			||||||
    groupCall.setMicrophoneMuted(!groupCall.isMicrophoneMuted());
 | 
					    groupCall.setMicrophoneMuted(!groupCall.isMicrophoneMuted());
 | 
				
			||||||
  }, [groupCall]);
 | 
					  }, [groupCall]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const toggleScreensharing = useCallback(() => {
 | 
					  const toggleScreensharing = useCallback(async () => {
 | 
				
			||||||
 | 
					    if (!groupCall.isScreensharing()) {
 | 
				
			||||||
 | 
					      // toggling on
 | 
				
			||||||
      updateState({ requestingScreenshare: true });
 | 
					      updateState({ requestingScreenshare: true });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (groupCall.isScreensharing()) {
 | 
					      try {
 | 
				
			||||||
      groupCall.setScreensharingEnabled(false).then(() => {
 | 
					        await groupCall.setScreensharingEnabled(true, {
 | 
				
			||||||
        updateState({ requestingScreenshare: false });
 | 
					          audio: true,
 | 
				
			||||||
 | 
					          throwOnFail: true,
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      widget.api.transport
 | 
					 | 
				
			||||||
        .send(ElementWidgetActions.Screenshare, {})
 | 
					 | 
				
			||||||
        .then(
 | 
					 | 
				
			||||||
          (reply: { desktopCapturerSourceId: string; failed?: boolean }) => {
 | 
					 | 
				
			||||||
            if (reply.failed) {
 | 
					 | 
				
			||||||
        updateState({ requestingScreenshare: false });
 | 
					        updateState({ requestingScreenshare: false });
 | 
				
			||||||
              return;
 | 
					      } catch (e) {
 | 
				
			||||||
            }
 | 
					        // this will fail in Electron because getDisplayMedia just throws a permission
 | 
				
			||||||
 | 
					        // error, so if we have a widget API, try requesting via that.
 | 
				
			||||||
            groupCall
 | 
					        if (widget) {
 | 
				
			||||||
              .setScreensharingEnabled(true, {
 | 
					          const reply = await widget.api.transport.send(
 | 
				
			||||||
                audio: !reply.desktopCapturerSourceId,
 | 
					            ElementWidgetActions.ScreenshareRequest,
 | 
				
			||||||
                desktopCapturerSourceId: reply.desktopCapturerSourceId,
 | 
					            {}
 | 
				
			||||||
              })
 | 
					 | 
				
			||||||
              .then(() => {
 | 
					 | 
				
			||||||
                updateState({ requestingScreenshare: false });
 | 
					 | 
				
			||||||
              });
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
          );
 | 
					          );
 | 
				
			||||||
 | 
					          if (!reply.pending) {
 | 
				
			||||||
 | 
					            updateState({ requestingScreenshare: false });
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      // toggling off
 | 
				
			||||||
 | 
					      groupCall.setScreensharingEnabled(false);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }, [groupCall]);
 | 
					  }, [groupCall]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const onScreenshareStart = useCallback(
 | 
				
			||||||
 | 
					    async (ev: CustomEvent<IWidgetApiRequest>) => {
 | 
				
			||||||
 | 
					      updateState({ requestingScreenshare: false });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const data = ev.detail.data as unknown as ScreenshareStartData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      await groupCall.setScreensharingEnabled(true, {
 | 
				
			||||||
 | 
					        desktopCapturerSourceId: data.desktopCapturerSourceId as string,
 | 
				
			||||||
 | 
					        audio: !data.desktopCapturerSourceId,
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      await widget.api.transport.reply(ev.detail, {});
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    [groupCall]
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const onScreenshareStop = useCallback(
 | 
				
			||||||
 | 
					    async (ev: CustomEvent<IWidgetApiRequest>) => {
 | 
				
			||||||
 | 
					      updateState({ requestingScreenshare: false });
 | 
				
			||||||
 | 
					      await groupCall.setScreensharingEnabled(false);
 | 
				
			||||||
 | 
					      await widget.api.transport.reply(ev.detail, {});
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    [groupCall]
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  useEffect(() => {
 | 
				
			||||||
 | 
					    if (widget) {
 | 
				
			||||||
 | 
					      widget.lazyActions.on(
 | 
				
			||||||
 | 
					        ElementWidgetActions.ScreenshareStart,
 | 
				
			||||||
 | 
					        onScreenshareStart
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					      widget.lazyActions.on(
 | 
				
			||||||
 | 
					        ElementWidgetActions.ScreenshareStop,
 | 
				
			||||||
 | 
					        onScreenshareStop
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return () => {
 | 
				
			||||||
 | 
					        widget.lazyActions.off(
 | 
				
			||||||
 | 
					          ElementWidgetActions.ScreenshareStart,
 | 
				
			||||||
 | 
					          onScreenshareStart
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        widget.lazyActions.off(
 | 
				
			||||||
 | 
					          ElementWidgetActions.ScreenshareStop,
 | 
				
			||||||
 | 
					          onScreenshareStop
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }, [onScreenshareStart, onScreenshareStop]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const { t } = useTranslation();
 | 
					  const { t } = useTranslation();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  useEffect(() => {
 | 
					  useEffect(() => {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -30,7 +30,21 @@ export enum ElementWidgetActions {
 | 
				
			||||||
  HangupCall = "im.vector.hangup",
 | 
					  HangupCall = "im.vector.hangup",
 | 
				
			||||||
  TileLayout = "io.element.tile_layout",
 | 
					  TileLayout = "io.element.tile_layout",
 | 
				
			||||||
  SpotlightLayout = "io.element.spotlight_layout",
 | 
					  SpotlightLayout = "io.element.spotlight_layout",
 | 
				
			||||||
  Screenshare = "io.element.screenshare",
 | 
					
 | 
				
			||||||
 | 
					  // Element Call -> host requesting to start a screenshare
 | 
				
			||||||
 | 
					  // (ie. expects a ScreenshareStart once the user has picked a source)
 | 
				
			||||||
 | 
					  // Element Call -> host requesting to start a screenshare
 | 
				
			||||||
 | 
					  // (ie. expects a ScreenshareStart once the user has picked a source)
 | 
				
			||||||
 | 
					  // replies with { pending } where pending is true if the host has asked
 | 
				
			||||||
 | 
					  // the user to choose a window and false if not (ie. if the host isn't
 | 
				
			||||||
 | 
					  // running within Electron)
 | 
				
			||||||
 | 
					  ScreenshareRequest = "io.element.screenshare_request",
 | 
				
			||||||
 | 
					  // host -> Element Call telling EC to start screen sharing with
 | 
				
			||||||
 | 
					  // the given source
 | 
				
			||||||
 | 
					  ScreenshareStart = "io.element.screenshare_start",
 | 
				
			||||||
 | 
					  // host -> Element Call telling EC to stop screen sharing, or that
 | 
				
			||||||
 | 
					  // the user cancelled when selecting a source after a ScreenshareRequest
 | 
				
			||||||
 | 
					  ScreenshareStop = "io.element.screenshare_stop",
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface JoinCallData {
 | 
					export interface JoinCallData {
 | 
				
			||||||
| 
						 | 
					@ -38,6 +52,10 @@ export interface JoinCallData {
 | 
				
			||||||
  videoInput: string | null;
 | 
					  videoInput: string | null;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface ScreenshareStartData {
 | 
				
			||||||
 | 
					  desktopCapturerSourceId: string;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface WidgetHelpers {
 | 
					interface WidgetHelpers {
 | 
				
			||||||
  api: WidgetApi;
 | 
					  api: WidgetApi;
 | 
				
			||||||
  lazyActions: LazyEventEmitter;
 | 
					  lazyActions: LazyEventEmitter;
 | 
				
			||||||
| 
						 | 
					@ -69,6 +87,8 @@ export const widget: WidgetHelpers | null = (() => {
 | 
				
			||||||
        ElementWidgetActions.HangupCall,
 | 
					        ElementWidgetActions.HangupCall,
 | 
				
			||||||
        ElementWidgetActions.TileLayout,
 | 
					        ElementWidgetActions.TileLayout,
 | 
				
			||||||
        ElementWidgetActions.SpotlightLayout,
 | 
					        ElementWidgetActions.SpotlightLayout,
 | 
				
			||||||
 | 
					        ElementWidgetActions.ScreenshareStart,
 | 
				
			||||||
 | 
					        ElementWidgetActions.ScreenshareStop,
 | 
				
			||||||
      ].forEach((action) => {
 | 
					      ].forEach((action) => {
 | 
				
			||||||
        api.on(`action:${action}`, (ev: CustomEvent<IWidgetApiRequest>) => {
 | 
					        api.on(`action:${action}`, (ev: CustomEvent<IWidgetApiRequest>) => {
 | 
				
			||||||
          ev.preventDefault();
 | 
					          ev.preventDefault();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8728,9 +8728,9 @@ matrix-events-sdk@^0.0.1-beta.7:
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/matrix-events-sdk/-/matrix-events-sdk-0.0.1-beta.7.tgz#5ffe45eba1f67cc8d7c2377736c728b322524934"
 | 
					  resolved "https://registry.yarnpkg.com/matrix-events-sdk/-/matrix-events-sdk-0.0.1-beta.7.tgz#5ffe45eba1f67cc8d7c2377736c728b322524934"
 | 
				
			||||||
  integrity sha512-9jl4wtWanUFSy2sr2lCjErN/oC8KTAtaeaozJtrgot1JiQcEI4Rda9OLgQ7nLKaqb4Z/QUx/fR3XpDzm5Jy1JA==
 | 
					  integrity sha512-9jl4wtWanUFSy2sr2lCjErN/oC8KTAtaeaozJtrgot1JiQcEI4Rda9OLgQ7nLKaqb4Z/QUx/fR3XpDzm5Jy1JA==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
"matrix-js-sdk@github:matrix-org/matrix-js-sdk#5a0787349d4951012eabe72f3363c17bdcda0d56":
 | 
					"matrix-js-sdk@github:matrix-org/matrix-js-sdk#1666419beaf08cafefdc34cced417cb0ba120b2e":
 | 
				
			||||||
  version "20.1.0"
 | 
					  version "20.1.0"
 | 
				
			||||||
  resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/5a0787349d4951012eabe72f3363c17bdcda0d56"
 | 
					  resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/1666419beaf08cafefdc34cced417cb0ba120b2e"
 | 
				
			||||||
  dependencies:
 | 
					  dependencies:
 | 
				
			||||||
    "@babel/runtime" "^7.12.5"
 | 
					    "@babel/runtime" "^7.12.5"
 | 
				
			||||||
    "@types/sdp-transform" "^2.4.5"
 | 
					    "@types/sdp-transform" "^2.4.5"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue