Как получать кадры из компонента <video></video>?

147
15 декабря 2019, 22:40

Скажите, пожалуйста, как мне получать кадры с видео, используя тег <video></video>, чтобы их можно было обработать и вывести на канвас?

Answer 1

При помощи WebGL/GLSL можно загружать кадры видео как текстуру и манипулировать полученными изображениями.

В примере ниже к текстуре применяется эффект во фрагментном шейдере,копируем картинку 3 раза, одну картинку мы не трогаем, вторую копию мы перевернем и сделаем негатив, а у третьей копии изменим цвет

let canvas = document.createElement('canvas'); 
canvas.width = 3*(canvas.height = 185); 
document.body.append(canvas); 
document.body.style.margin = 0; 
 
let gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); 
let texture = initTexture(gl); 
let video = setupVideo('https://i.imgur.com/Pfa2ZxZ.mp4'); 
let pid = gl.createProgram(); 
 
shader(` 
  attribute vec2 coords; 
    void main(void) { 
    gl_Position = vec4(coords.xy, 0.0, 1.0); 
  } 
`, gl.VERTEX_SHADER); 
 
shader(` 
  precision highp float; 
  uniform sampler2D texture; 
 
  vec4 sample(vec2 uv) { 
      return texture2D(texture, uv); 
  } 
 
  vec4 frag (vec2 uv) { 
   
    if (uv.x > 0.333 && uv.x < 0.666) { 
        uv.x *= 3.; 
        uv.x -= 1.; 
        return vec4(1.0 - sample(1.0-uv).xyz, 1.0); 
    } 
     
    if (uv.x >= 0.666) { 
        uv.x *= 3.; 
        uv.x -= 2.; 
        return vec4(1.0 - sample(uv).zxy, 1.0); 
    } 
     
    uv.x *= 3.; 
    return sample(uv); 
  } 
 
  void main(void) { 
      gl_FragColor = frag(vec2( 
        gl_FragCoord.x / ${canvas.width}., 
        1. - gl_FragCoord.y / ${canvas.height}. 
      )); 
  } 
`, gl.FRAGMENT_SHADER); 
 
gl.linkProgram(pid); 
gl.useProgram(pid); 
 
let array = new Float32Array([-1,  3, -1, -1, 3, -1]); 
gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer()); 
gl.bufferData(gl.ARRAY_BUFFER, array, gl.STATIC_DRAW); 
 
let al = gl.getAttribLocation(pid, "coords"); 
gl.vertexAttribPointer(al, 2, gl.FLOAT, false, 0, 0); 
gl.enableVertexAttribArray(al); 
 
var textureLocation = gl.getUniformLocation(pid, "texture"); 
gl.uniform1i(textureLocation, 0); 
 
function shader(src, type) { 
    let sid = gl.createShader(type); 
    gl.shaderSource(sid, src); 
    gl.compileShader(sid); 
    var message = gl.getShaderInfoLog(sid); 
    gl.attachShader(pid, sid); 
    if (message.length > 0) { 
        console.log(src.split('\n').map(function (str, i) { 
            return ("" + (1 + i)).padStart(4, "0") + ": " + str 
        }).join('\n')); 
        throw message; 
    } 
} 
 
function drawTriangle() { 
    gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); 
    gl.clearColor(0, 0, 0, 0); 
    gl.drawArrays(gl.TRIANGLES, 0, 3); 
} 
 
function render(now) { 
    updateTexture(gl, texture, video); 
    drawTriangle(); 
    requestAnimationFrame(render); 
} 
 
requestAnimationFrame(render); 
 
function setupVideo(url) { 
    const video = document.createElement('video'); 
    var playing = false; 
    var timeupdate = false; 
    video.crossOrigin = "anonymous"; 
    video.autoplay = true; 
    video.muted = true; 
    video.loop = true; 
    video.addEventListener('playing', function() { 
        playing = true; 
        checkReady(); 
    }, true); 
    video.addEventListener('timeupdate', function() { 
        timeupdate = true; 
        checkReady(); 
    }, true); 
    video.src = url; 
    video.play(); 
 
    function checkReady() { 
        if (playing && timeupdate)  
            copyVideo = true;   
    } 
    return video; 
} 
 
function initTexture(gl) { 
    const texture = gl.createTexture(); 
    gl.bindTexture(gl.TEXTURE_2D, texture); 
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA,1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, 
        new Uint8Array([0, 0, 0, 255])); 
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); 
    return texture; 
} 
 
function updateTexture(gl, texture, video) { 
    const level = 0; 
    const internalFormat = gl.RGBA; 
    const srcFormat = gl.RGBA; 
    const srcType = gl.UNSIGNED_BYTE; 
    gl.bindTexture(gl.TEXTURE_2D, texture); 
    gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, 
        srcFormat, srcType, video); 
}

Answer 2

const 
video = document.getElementById ('video1'), 
canvas = document.getElementById ('canvas1') 
 
 
video.onclick = () => { 
 
  canvas.width = video.videoWidth 
  canvas.height = video.videoHeight 
   
  canvas.getContext ('2d').drawImage (video, 0, 0) 
}
<video id="video1" width="100" autoplay loop> 
<source src="https://i.imgur.com/Pfa2ZxZ.mp4"></source> 
</video> 
<canvas id="canvas1"></canvas>

READ ALSO
Как сделать слайды разного размера на slick?

Как сделать слайды разного размера на slick?

Кто нибудь сталкивался с подобной задачей, помогите решить задачу

156
Получить переменную из URL

Получить переменную из URL

На страницу можно попасть кликнув на одну из ссылок, в которых прописаны якоряТо есть при попадании на страницу её URL может иметь вот такой...

172
WebSocketService - закрытие соединения с причиной

WebSocketService - закрытие соединения с причиной

Использую класс наследник MicrosoftServiceModel

171