feat: Ability to record videos and capture photos directly in app by selecting + in Files view (#2095)

This commit is contained in:
Aman Harwara
2022-12-13 21:41:41 +05:30
committed by GitHub
parent cd8596b14e
commit d4b63e4ea6
11 changed files with 552 additions and 39 deletions

View File

@@ -101,17 +101,17 @@ export class MomentsService extends AbstractViewController {
}
const filename = `Moment ${dateToStringStyle1(new Date())}.png`
const camera = new PhotoRecorder(filename)
const camera = new PhotoRecorder()
await camera.initialize()
if (this.application.isMobileDevice) {
await sleep(DELAY_AFTER_STARTING_CAMERA_TO_ALLOW_MOBILE_AUTOFOCUS)
}
let file = await camera.takePhoto()
let file = await camera.takePhoto(filename)
if (!file) {
await sleep(1000)
file = await camera.takePhoto()
file = await camera.takePhoto(filename)
if (!file) {
return undefined
}

View File

@@ -1,20 +1,52 @@
export class PhotoRecorder {
public video!: HTMLVideoElement
public devices!: MediaDeviceInfo[]
public selectedDevice!: MediaDeviceInfo
private canvas!: HTMLCanvasElement
private width!: number
private height!: number
private stream!: MediaStream
constructor(private fileName: string) {}
// eslint-disable-next-line @typescript-eslint/no-empty-function
constructor() {}
public static async isSupported(): Promise<boolean> {
const devices = await navigator.mediaDevices.enumerateDevices()
const hasCamera = devices.some((device) => device.kind === 'videoinput')
return hasCamera
}
public async setDevice(deviceId: string) {
this.selectedDevice = this.devices.find((device) => device.deviceId === deviceId) ?? this.devices[0]
this.stream = await navigator.mediaDevices.getUserMedia({
video: {
deviceId: this.selectedDevice.deviceId,
},
audio: false,
})
this.video.srcObject = this.stream
await this.video.play()
await this.awaitVideoReady(this.video)
}
public async initialize() {
this.stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: false })
this.devices = (await navigator.mediaDevices.enumerateDevices()).filter((device) => device.kind === 'videoinput')
this.selectedDevice = this.devices[0]
this.stream = await navigator.mediaDevices.getUserMedia({
video: {
deviceId: this.selectedDevice.deviceId,
},
audio: false,
})
this.video = document.createElement('video')
this.video.playsInline = true
this.video.style.position = 'absolute'
this.video.style.display = 'none'
this.video.oncontextmenu = (e) => e.preventDefault()
this.canvas = document.createElement('canvas')
@@ -33,7 +65,7 @@ export class PhotoRecorder {
this.canvas.height = this.height
}
public async takePhoto(): Promise<File | undefined> {
public async takePhoto(fileName: string): Promise<File | undefined> {
const context = this.canvas.getContext('2d')
context?.drawImage(this.video, 0, 0, this.width, this.height)
const dataUrl = this.canvas.toDataURL('image/png')
@@ -46,7 +78,7 @@ export class PhotoRecorder {
const res: Response = await fetch(dataUrl)
const blob: Blob = await res.blob()
const file = new File([blob], this.fileName, { type: 'image/png' })
const file = new File([blob], fileName, { type: 'image/png' })
return file
}

View File

@@ -13,6 +13,12 @@ export class VideoRecorder {
constructor(private fileName: string) {}
public static async isSupported(): Promise<boolean> {
const devices = await navigator.mediaDevices.enumerateDevices()
const hasCamera = devices.some((device) => device.kind === 'videoinput')
return hasCamera
}
public async initialize() {
this.stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true })
this.recorder = new MediaRecorder(this.stream)
@@ -58,7 +64,9 @@ export class VideoRecorder {
public async stop(): Promise<File> {
this.video.pause()
this.recorder.stop()
if (this.recorder.state !== 'inactive') {
this.recorder.stop()
}
this.video.parentElement?.removeChild(this.video)
this.canvas.parentElement?.removeChild(this.canvas)