UNPKG

@cornerstonejs/core

Version:
142 lines (141 loc) 5.45 kB
import ViewportType from '../../../enums/ViewportType.js'; import { normalizeVideoPlaybackInfo } from '../../../utilities/VideoUtilities.js'; import { resolveVideoCanvasMapping } from './videoViewportCamera.js'; export class HtmlVideoRenderPath { async addData(ctx, data, options) { const videoData = data; const payload = videoData; const element = document.createElement('video'); element.src = payload.renderedUrl; element.preload = 'auto'; element.crossOrigin = 'anonymous'; element.playsInline = true; element.style.position = 'absolute'; element.style.left = '0'; element.style.top = '0'; element.style.transformOrigin = 'top left'; element.style.objectFit = 'fill'; await new Promise((resolve) => { const onLoadedMetadata = () => { element.removeEventListener('loadedmetadata', onLoadedMetadata); resolve(); }; element.addEventListener('loadedmetadata', onLoadedMetadata); ctx.element.appendChild(element); }); const playbackInfo = normalizeVideoPlaybackInfo({ durationSeconds: element.duration, cineRate: payload.fps, numberOfFrames: payload.numberOfFrames, }); const normalizedPayload = { ...payload, durationSeconds: element.duration, fps: playbackInfo.fps, numberOfFrames: playbackInfo.numberOfFrames, frameRange: playbackInfo.frameRange, }; Object.assign(videoData, normalizedPayload); const rendering = { renderMode: 'video2d', element, }; return { rendering, updateDataPresentation: (props) => { this.updateDataPresentation(rendering, props); }, applyViewState: (camera) => { this.applyViewState(rendering, camera, videoData); }, getFrameOfReferenceUID: () => { return this.getFrameOfReferenceUID(rendering); }, removeData: () => { this.removeData(rendering); }, }; } updateDataPresentation(rendering, props) { const videoProps = props; const { element } = rendering; element.style.display = videoProps?.visible === false ? 'none' : ''; element.style.opacity = String(videoProps?.opacity ?? 1); element.loop = videoProps?.loop ?? true; element.muted = videoProps?.muted ?? true; element.playbackRate = videoProps?.playbackRate ?? 1; element.style.objectFit = videoProps?.objectFit ?? 'contain'; } applyViewState(rendering, camera, data) { const videoCamera = camera; const { element } = rendering; const rotation = videoCamera.rotation ?? 0; const mapping = this.getCanvasMapping(element, videoCamera); rendering.currentCamera = videoCamera; if (mapping) { element.style.width = `${mapping.width}px`; element.style.height = `${mapping.height}px`; element.style.left = `${mapping.left}px`; element.style.top = `${mapping.top}px`; } element.style.transform = `rotate(${rotation}deg)`; if (typeof videoCamera.currentTimeSeconds === 'number' && Math.abs(element.currentTime - videoCamera.currentTimeSeconds) > 0.5 / Math.max(1, data.fps)) { element.currentTime = videoCamera.currentTimeSeconds; } } canvasToWorld(rendering, canvasPos) { const mapping = this.getCanvasMapping(rendering.element, rendering.currentCamera); if (!mapping) { return [0, 0, 0]; } return [ (canvasPos[0] - mapping.left) / mapping.worldToCanvasRatio, (canvasPos[1] - mapping.top) / mapping.worldToCanvasRatio, 0, ]; } worldToCanvas(rendering, worldPos) { const mapping = this.getCanvasMapping(rendering.element, rendering.currentCamera); if (!mapping) { return [0, 0]; } return [ mapping.left + worldPos[0] * mapping.worldToCanvasRatio, mapping.top + worldPos[1] * mapping.worldToCanvasRatio, ]; } getCanvasMapping(element, camera) { const container = element.parentElement; return resolveVideoCanvasMapping({ containerWidth: container?.clientWidth ?? 0, containerHeight: container?.clientHeight ?? 0, intrinsicWidth: element.videoWidth || container?.clientWidth || 0, intrinsicHeight: element.videoHeight || container?.clientHeight || 0, objectFit: element.style.objectFit, camera, }); } getFrameOfReferenceUID(rendering) { const { element } = rendering; return element.currentSrc || element.src; } removeData(rendering) { const { element } = rendering; element.pause(); element.remove(); } } export class HtmlVideoPath { constructor() { this.id = 'video:html-element'; this.type = ViewportType.VIDEO_NEXT; } matches(data, options) { return data.type === 'video' && options.renderMode === 'video2d'; } createRenderPath() { return new HtmlVideoRenderPath(); } }