mirror of
https://github.com/hcengineering/platform.git
synced 2025-05-13 10:53:06 +00:00
qfix: add support for recording videos from desktop (#8306)
This commit is contained in:
parent
e70c86f86f
commit
c12fee1bb2
@ -115,6 +115,7 @@ function hookOpenWindow (window: BrowserWindow): void {
|
||||
devTools: true,
|
||||
sandbox: false,
|
||||
partition: sessionPartition,
|
||||
nodeIntegration: true,
|
||||
preload: path.join(app.getAppPath(), 'dist', 'main', 'preload.js'),
|
||||
additionalArguments: [
|
||||
`--open=${encodeURI(
|
||||
@ -192,6 +193,7 @@ const createWindow = async (): Promise<void> => {
|
||||
webPreferences: {
|
||||
devTools: true,
|
||||
sandbox: false,
|
||||
nodeIntegration: true,
|
||||
backgroundThrottling: false,
|
||||
partition: sessionPartition,
|
||||
preload: path.join(app.getAppPath(), 'dist', 'main', 'preload.js')
|
||||
|
@ -19,10 +19,11 @@ import { workbenchId, logOut } from '@hcengineering/workbench'
|
||||
|
||||
import { isOwnerOrMaintainer } from '@hcengineering/core'
|
||||
import { configurePlatform } from './platform'
|
||||
import { defineScreenShare } from './screenShare'
|
||||
import { defineScreenShare, defineScreenRecorder } from './screenShare'
|
||||
import { IPCMainExposed } from './types'
|
||||
|
||||
defineScreenShare()
|
||||
defineScreenRecorder()
|
||||
|
||||
void configurePlatform().then(() => {
|
||||
createApp(document.body)
|
||||
|
@ -5,6 +5,54 @@ import { showPopup } from '@hcengineering/ui'
|
||||
import { Track, LocalTrack, LocalAudioTrack, LocalVideoTrack, ParticipantEvent, TrackInvalidError, ScreenShareCaptureOptions, DeviceUnsupportedError, ScreenSharePresets } from 'livekit-client'
|
||||
|
||||
import { IPCMainExposed } from './types'
|
||||
import { setMetadata } from '@hcengineering/platform'
|
||||
import recordPlugin from '@hcengineering/recorder'
|
||||
|
||||
export async function getMediaStream (opts?: DisplayMediaStreamOptions): Promise<MediaStream> {
|
||||
if (opts === undefined) {
|
||||
throw new Error('opts must be provided')
|
||||
}
|
||||
const ipcMain = (window as any).electron as IPCMainExposed
|
||||
const sources = await ipcMain.getScreenSources()
|
||||
|
||||
const hasAccess = await ipcMain.getScreenAccess()
|
||||
if (!hasAccess) {
|
||||
log.error('No screen access granted')
|
||||
throw new Error('No screen access granted')
|
||||
}
|
||||
|
||||
if (navigator.mediaDevices.getDisplayMedia === undefined) {
|
||||
throw new DeviceUnsupportedError('getDisplayMedia not supported')
|
||||
}
|
||||
return await new Promise<MediaStream>((resolve, reject) => {
|
||||
showPopup(
|
||||
love.component.SelectScreenSourcePopup,
|
||||
{
|
||||
sources
|
||||
},
|
||||
'top',
|
||||
() => {
|
||||
reject(new Error('No source selected'))
|
||||
},
|
||||
(val) => {
|
||||
if (val != null) {
|
||||
opts.video = {
|
||||
mandatory: {
|
||||
...(typeof opts.video === 'boolean' ? {} : opts.video),
|
||||
chromeMediaSource: 'desktop',
|
||||
chromeMediaSourceId: val
|
||||
}
|
||||
} as any
|
||||
resolve(window.navigator.mediaDevices.getUserMedia(opts))
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
export function defineScreenRecorder (): void {
|
||||
setMetadata(recordPlugin.metadata.GetCustomMediaStream, getMediaStream)
|
||||
}
|
||||
|
||||
export function defineScreenShare (): void {
|
||||
setCustomCreateScreenTracks(async function electronCreateScreenTracks (options?: ScreenShareCaptureOptions) {
|
||||
|
@ -91,8 +91,10 @@
|
||||
if (state === RecordingState.Inactive) {
|
||||
try {
|
||||
await createScreenRecorder()
|
||||
} catch {
|
||||
} catch (err) {
|
||||
console.log('cant create screen recorder', err)
|
||||
distpacher('close', true)
|
||||
return
|
||||
}
|
||||
await showCountdown()
|
||||
recorder?.start()
|
||||
|
@ -16,6 +16,8 @@
|
||||
import { type Resources } from '@hcengineering/platform'
|
||||
import { record } from './recording'
|
||||
|
||||
export { ScreenRecorder } from './screen-recorder'
|
||||
|
||||
export default async (): Promise<Resources> => ({
|
||||
function: {
|
||||
Record: record
|
||||
|
@ -13,8 +13,10 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import plugin from '@hcengineering/recorder'
|
||||
import { Recorder } from './recorder'
|
||||
import { TusUploader, type Uploader, type Options } from './uploader'
|
||||
import { getMetadata } from '@hcengineering/platform'
|
||||
|
||||
export class ScreenRecorder {
|
||||
private readonly recorder: Recorder
|
||||
@ -29,11 +31,20 @@ export class ScreenRecorder {
|
||||
let width = 0
|
||||
let height = 0
|
||||
const combinedStream = new MediaStream()
|
||||
const displayStream = await navigator.mediaDevices.getDisplayMedia({
|
||||
video: { frameRate: opts.fps ?? 30 },
|
||||
audio: true
|
||||
const getMediaStream =
|
||||
getMetadata(plugin.metadata.GetCustomMediaStream) ??
|
||||
(async (op) => await navigator.mediaDevices.getDisplayMedia(op))
|
||||
const displayStream = await getMediaStream({
|
||||
video: { frameRate: opts.fps ?? 30 }
|
||||
})
|
||||
const microphoneStream = await navigator.mediaDevices.getUserMedia({ audio: true })
|
||||
try {
|
||||
const microphoneStream = await navigator.mediaDevices.getUserMedia({ audio: true })
|
||||
microphoneStream.getAudioTracks().forEach((track) => {
|
||||
combinedStream.addTrack(track)
|
||||
})
|
||||
} catch (err) {
|
||||
console.warn('microphone is disabled', err)
|
||||
}
|
||||
displayStream.getVideoTracks().forEach((track) => {
|
||||
combinedStream.addTrack(track)
|
||||
width = Math.max(track.getSettings().width ?? width, width)
|
||||
@ -42,9 +53,6 @@ export class ScreenRecorder {
|
||||
displayStream.getAudioTracks().forEach((track) => {
|
||||
combinedStream.addTrack(track)
|
||||
})
|
||||
microphoneStream.getAudioTracks().forEach((track) => {
|
||||
combinedStream.addTrack(track)
|
||||
})
|
||||
|
||||
const recorder = new Recorder(combinedStream)
|
||||
const uploader = new TusUploader(recorder.asStream(), { ...opts, metadata: { resolution: width + ':' + height } })
|
||||
|
@ -19,6 +19,11 @@ import { type UploadHandler } from '@hcengineering/uploader'
|
||||
*/
|
||||
export const recorderId = 'recorder' as Plugin
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type GetMediaStream = (options?: DisplayMediaStreamOptions) => Promise<MediaStream>
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@ -27,7 +32,8 @@ const recordPlugin = plugin(recorderId, {
|
||||
Record: '' as Asset
|
||||
},
|
||||
metadata: {
|
||||
StreamUrl: '' as Metadata<string>
|
||||
StreamUrl: '' as Metadata<string>,
|
||||
GetCustomMediaStream: '' as Metadata<GetMediaStream>
|
||||
},
|
||||
string: {
|
||||
Pause: '' as IntlString,
|
||||
|
Loading…
Reference in New Issue
Block a user