All Downloads are FREE. Search and download functionalities are using the official Maven repository.

META-INF.resources.frontend.webapi.video.ts Maven / Gradle / Ivy

import { LitElement, html } from 'lit';
import { customElement, property, query } from 'lit/decorators.js';

interface SourceDto {
    src: string,
    type: string
};

function compareSourceDtoArrays(a: SourceDto[] | null, b: SourceDto[] | null): boolean {
    // If both are null
    if(a == null && b == null) {
        return false;
    }
    // If only one of them is null
    if(a == null || b == null) {
        return true;
    }
    // If they don't have the same length
    if(a.length !== b.length) {
        return true;
    }
    // If one element doesn't match their counterpart
    for(let i = 0; i < a.length; i++) {
        if(a[i].src !== b[i].src || a[i].type !== b[i].type) {
            return true;
        }
    }
    // Otherwise nothing changed
    return false;
}

@customElement('rap-video')
export class RapVideo extends LitElement {
    
    @property()
    public controls: boolean = true;
    @query('#rapvideo')
    private rapvideo: HTMLVideoElement;
    private recorder: MediaRecorder | null;
    private _sources: SourceDto[] = [];

    // TODO: Find out why the video is only rendered after play() is called
    @property({ hasChanged: compareSourceDtoArrays })
    get sources(): SourceDto[] { return this._sources; }
    set sources(newSources: readonly SourceDto[]) {
        this._sources.length = 0;
        this._sources.push(...newSources);
    }

    constructor() {
        super();
    }
    
    render() {
		return html`
			
		`;
	}

    addDeviceCameraVideoSource(withVideo: boolean, withAudio: boolean, withFrontCamera: boolean): void {
        let video: any = false;

        if(withVideo) video = { facingMode: { ideal: withFrontCamera ? 'user' : 'environment' }};

        navigator.mediaDevices
            .getUserMedia({ audio: withAudio, video })
            .then(s => this.rapvideo.srcObject = s);
    }

    startRecording(): void {
        const recorder = new MediaRecorder(this.rapvideo.srcObject as MediaStream);
        
        let isFirstChunk = true;

        // When a chunk becomes available send it to the server
        recorder.ondataavailable = e => {
            const reader = new FileReader();
            reader.onloadend = () => this.$server.receiveRecordChunk(reader.result);
            reader.readAsDataURL(e.data);

            if(isFirstChunk) {
                isFirstChunk = false;
                this.$server.onRecordStart(recorder.mimeType)
            }
        }

        // Record 5 second chunks instead of one massive blob that might not be able to be transfered to the server
        recorder.start(5000);
        
        this.recorder = recorder;
    }

    stopRecording(): void {
        if(this.recorder !== null) {
            this.recorder.stop();
            this.recorder = null;
        }
    }
    
    play() {
    	this.rapvideo.play();
    }
    
    pause() {
    	this.rapvideo.pause();
    }

    takePicture() {
        if(this.rapvideo.srcObject === null) {
            return;
        }

        let canvas = document.createElement('canvas');
        canvas.width = this.rapvideo.videoWidth;
        canvas.height = this.rapvideo.videoHeight;
        canvas.getContext('2d')!.drawImage(this.rapvideo, 0, 0, this.rapvideo.videoWidth, this.rapvideo.videoHeight);
        canvas.toBlob(b => {
            const reader = new FileReader();
            reader.onloadend = () => this.$server.onPictureReceived(reader.result);
            reader.readAsDataURL(b!);
        });
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy