Merge pull request #5707 from matrix-org/dbkr/audiostream
Option for audio streaming
This commit is contained in:
commit
d71999114b
4 changed files with 83 additions and 1 deletions
55
src/Livestream.ts
Normal file
55
src/Livestream.ts
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ClientWidgetApi } from "matrix-widget-api";
|
||||||
|
import { MatrixClientPeg } from "./MatrixClientPeg";
|
||||||
|
import SdkConfig from "./SdkConfig";
|
||||||
|
import { ElementWidgetActions } from "./stores/widgets/ElementWidgetActions";
|
||||||
|
|
||||||
|
export function getConfigLivestreamUrl() {
|
||||||
|
return SdkConfig.get()["audioStreamUrl"];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dummy rtmp URL used to signal that we want a special audio-only stream
|
||||||
|
const AUDIOSTREAM_DUMMY_URL = 'rtmp://audiostream.dummy/';
|
||||||
|
|
||||||
|
async function createLiveStream(roomId: string) {
|
||||||
|
const openIdToken = await MatrixClientPeg.get().getOpenIdToken();
|
||||||
|
|
||||||
|
const url = getConfigLivestreamUrl() + "/createStream";
|
||||||
|
|
||||||
|
const response = await window.fetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
room_id: roomId,
|
||||||
|
openid_token: openIdToken,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const respBody = await response.json();
|
||||||
|
return respBody['stream_id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function startJitsiAudioLivestream(widgetMessaging: ClientWidgetApi, roomId: string) {
|
||||||
|
const streamId = await createLiveStream(roomId);
|
||||||
|
|
||||||
|
await widgetMessaging.transport.send(ElementWidgetActions.StartLiveStream, {
|
||||||
|
rtmpStreamKey: AUDIOSTREAM_DUMMY_URL + streamId,
|
||||||
|
});
|
||||||
|
}
|
|
@ -28,9 +28,11 @@ import dis from "../../../dispatcher/dispatcher";
|
||||||
import SettingsStore from "../../../settings/SettingsStore";
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
import Modal from "../../../Modal";
|
import Modal from "../../../Modal";
|
||||||
import QuestionDialog from "../dialogs/QuestionDialog";
|
import QuestionDialog from "../dialogs/QuestionDialog";
|
||||||
|
import ErrorDialog from "../dialogs/ErrorDialog";
|
||||||
import {WidgetType} from "../../../widgets/WidgetType";
|
import {WidgetType} from "../../../widgets/WidgetType";
|
||||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||||
import { Container, WidgetLayoutStore } from "../../../stores/widgets/WidgetLayoutStore";
|
import { Container, WidgetLayoutStore } from "../../../stores/widgets/WidgetLayoutStore";
|
||||||
|
import { getConfigLivestreamUrl, startJitsiAudioLivestream } from "../../../Livestream";
|
||||||
|
|
||||||
interface IProps extends React.ComponentProps<typeof IconizedContextMenu> {
|
interface IProps extends React.ComponentProps<typeof IconizedContextMenu> {
|
||||||
app: IApp;
|
app: IApp;
|
||||||
|
@ -54,6 +56,27 @@ const WidgetContextMenu: React.FC<IProps> = ({
|
||||||
const widgetMessaging = WidgetMessagingStore.instance.getMessagingForId(app.id);
|
const widgetMessaging = WidgetMessagingStore.instance.getMessagingForId(app.id);
|
||||||
const canModify = userWidget || WidgetUtils.canUserModifyWidgets(roomId);
|
const canModify = userWidget || WidgetUtils.canUserModifyWidgets(roomId);
|
||||||
|
|
||||||
|
let streamAudioStreamButton;
|
||||||
|
if (getConfigLivestreamUrl() && WidgetType.JITSI.matches(app.type)) {
|
||||||
|
const onStreamAudioClick = async () => {
|
||||||
|
try {
|
||||||
|
await startJitsiAudioLivestream(widgetMessaging, roomId);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Failed to start livestream", err);
|
||||||
|
// XXX: won't i18n well, but looks like widget api only support 'message'?
|
||||||
|
const message = err.message || _t("Unable to start audio streaming.");
|
||||||
|
Modal.createTrackedDialog('WidgetContext Menu', 'Livestream failed', ErrorDialog, {
|
||||||
|
title: _t('Failed to start livestream'),
|
||||||
|
description: message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
onFinished();
|
||||||
|
};
|
||||||
|
streamAudioStreamButton = <IconizedContextMenuOption
|
||||||
|
onClick={onStreamAudioClick} label={_t("Start audio stream")}
|
||||||
|
/>;
|
||||||
|
}
|
||||||
|
|
||||||
let unpinButton;
|
let unpinButton;
|
||||||
if (showUnpin) {
|
if (showUnpin) {
|
||||||
const onUnpinClick = () => {
|
const onUnpinClick = () => {
|
||||||
|
@ -163,6 +186,7 @@ const WidgetContextMenu: React.FC<IProps> = ({
|
||||||
|
|
||||||
return <IconizedContextMenu {...props} chevronFace={ChevronFace.None} onFinished={onFinished}>
|
return <IconizedContextMenu {...props} chevronFace={ChevronFace.None} onFinished={onFinished}>
|
||||||
<IconizedContextMenuOptionList>
|
<IconizedContextMenuOptionList>
|
||||||
|
{ streamAudioStreamButton }
|
||||||
{ editButton }
|
{ editButton }
|
||||||
{ revokeButton }
|
{ revokeButton }
|
||||||
{ deleteButton }
|
{ deleteButton }
|
||||||
|
@ -175,4 +199,3 @@ const WidgetContextMenu: React.FC<IProps> = ({
|
||||||
};
|
};
|
||||||
|
|
||||||
export default WidgetContextMenu;
|
export default WidgetContextMenu;
|
||||||
|
|
||||||
|
|
|
@ -2410,6 +2410,9 @@
|
||||||
"Set status": "Set status",
|
"Set status": "Set status",
|
||||||
"Set a new status...": "Set a new status...",
|
"Set a new status...": "Set a new status...",
|
||||||
"View Community": "View Community",
|
"View Community": "View Community",
|
||||||
|
"Unable to start audio streaming.": "Unable to start audio streaming.",
|
||||||
|
"Failed to start livestream": "Failed to start livestream",
|
||||||
|
"Start audio stream": "Start audio stream",
|
||||||
"Take a picture": "Take a picture",
|
"Take a picture": "Take a picture",
|
||||||
"Delete Widget": "Delete Widget",
|
"Delete Widget": "Delete Widget",
|
||||||
"Deleting a widget removes it for all users in this room. Are you sure you want to delete this widget?": "Deleting a widget removes it for all users in this room. Are you sure you want to delete this widget?",
|
"Deleting a widget removes it for all users in this room. Are you sure you want to delete this widget?": "Deleting a widget removes it for all users in this room. Are you sure you want to delete this widget?",
|
||||||
|
|
|
@ -19,6 +19,7 @@ import { IWidgetApiRequest } from "matrix-widget-api";
|
||||||
export enum ElementWidgetActions {
|
export enum ElementWidgetActions {
|
||||||
ClientReady = "im.vector.ready",
|
ClientReady = "im.vector.ready",
|
||||||
HangupCall = "im.vector.hangup",
|
HangupCall = "im.vector.hangup",
|
||||||
|
StartLiveStream = "im.vector.start_live_stream",
|
||||||
OpenIntegrationManager = "integration_manager_open",
|
OpenIntegrationManager = "integration_manager_open",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue