Замена src у video, которое рисуется в canvas

152
16 октября 2019, 20:20

Есть canvas, который рисует video. Я хочу поменять источник видео и пока делаю это просто изменением атрибута src. Я не понимаю, что в таком случае происходит с канвасом, но при каждой смене источника (или load у видео) производительность падает, а нагрузка на CPU растет. Очевидно, делаю что-то не правильно.

Собственно, вопрос - почему так происходит и как правильно менять источник видео, чтобы избежать возможных проблем с производительностью?

P.S В сниппете канвас маленький, лагать начинает после 30+ замен, в проекте канвас на весь экран и их две штуки, там это заметно уже после 6-8 замены.

videos = new Array(); 
 
videos[0] = { 
  source: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4" 
}; 
videos[1] = { 
  source: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerEscapes.mp4" 
}; 
videos[2] = { 
  source: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerFun.mp4" 
}; 
 
var setSource = function(num) { 
  $('video').attr('src', videos[num].source); 
} 
 
var loadVideo = function() { 
  var i = 0; 
  var loadTimer = setInterval(function() { 
    i += 1; 
    $('video').get(0).load(); 
    if (i == 41) { 
      clearInterval(loadTimer); 
    }; 
  }, 100); 
} 
 
const canvas = document.querySelector('canvas'); 
const ctx = canvas.getContext("2d"); 
const video = document.querySelector('video'); 
 
video.addEventListener('play', () => { 
  function step() { 
    ctx.drawImage(video, 0, 0, canvas.width, canvas.height); 
    requestAnimationFrame(step); 
  } 
  requestAnimationFrame(step); 
}) 
 
let frameCount = function _fc(timeStart) { 
  let now = performance.now(); 
  let duration = now - timeStart; 
  if (duration < 1000) { 
    _fc.counter++; 
  } else { 
    _fc.fps = _fc.counter; 
    _fc.counter = 0; 
    timeStart = now; 
    $("#fps-counter").html(_fc.fps); 
    if (_fc.fps >= 55) { 
      $("#fps-counter").css("color", "green"); 
    } else if (_fc.fps >= 30) { 
      $("#fps-counter").css("color", "orange"); 
    } else if (_fc.fps < 30) { 
      $("#fps-counter").css("color", "red"); 
    } 
  } 
  requestAnimationFrame(() => frameCount(timeStart)); 
}; 
 
frameCount.counter = 0; 
frameCount.fps = 0; 
frameCount(performance.now());
canvas, video { 
  position: absolute; 
  right: 0; 
  margin: auto; 
} 
 
canvas { 
  top: 0; 
  width: 300px; 
  border: 1px solid red; 
} 
 
video { 
  bottom: 0; 
  width: 100px; 
  border: 1px solid blue; 
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> 
 
<p>Set video source</p> 
<button onclick="setSource(0)">1</button> 
<button onclick="setSource(1)">2</button> 
<button onclick="setSource(2)">3</button> 
<p>video.load() x40</p> 
<button onclick="loadVideo()">load()</button> 
<p> fps: <span id='fps-counter'>0</span></p> 
 
<video autoplay muted loop src='https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4'></video> 
 
<canvas></canvas>

Answer 1

Дело в событии play. При каждой смене источника или загрузке это событие вызывается. Соответственно, у вас очень много одинаковых вызовов функции step. Из-за этого и происходит утечка.

Способ решения в примере - просто proof of concept. В реальном коде надо будет сделать как-то по другому.

videos = new Array(); 
 
videos[0] = { 
  source: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4" 
}; 
videos[1] = { 
  source: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerEscapes.mp4" 
}; 
videos[2] = { 
  source: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerFun.mp4" 
}; 
 
var setSource = function(num) { 
  $('video').attr('src', videos[num].source); 
} 
 
var loadVideo = function() { 
  var i = 0; 
  var loadTimer = setInterval(function() { 
    i += 1; 
    $('video').get(0).load(); 
    if (i == 41) { 
      clearInterval(loadTimer); 
    }; 
  }, 100); 
} 
 
const canvas = document.querySelector('canvas'); 
const ctx = canvas.getContext("2d"); 
const video = document.querySelector('video'); 
let isPlaying = false; 
video.addEventListener('play', () => { 
  if (!isPlaying) { 
    console.log('play'); 
    requestAnimationFrame(step); 
    isPlaying = true; 
  } 
}); 
 
function step() { 
  ctx.drawImage(video, 0, 0, canvas.width, canvas.height); 
  requestAnimationFrame(step); 
} 
 
 
let frameCount = function _fc(timeStart) { 
  let now = performance.now(); 
  let duration = now - timeStart; 
  if (duration < 1000) { 
    _fc.counter++; 
  } else { 
    _fc.fps = _fc.counter; 
    _fc.counter = 0; 
    timeStart = now; 
    $("#fps-counter").html(_fc.fps); 
    if (_fc.fps >= 55) { 
      $("#fps-counter").css("color", "green"); 
    } else if (_fc.fps >= 30) { 
      $("#fps-counter").css("color", "orange"); 
    } else if (_fc.fps < 30) { 
      $("#fps-counter").css("color", "red"); 
    } 
  } 
  requestAnimationFrame(() => frameCount(timeStart)); 
}; 
 
frameCount.counter = 0; 
frameCount.fps = 0; 
frameCount(performance.now());
canvas, 
video { 
  position: absolute; 
  right: 0; 
  margin: auto; 
} 
 
canvas { 
  top: 0; 
  width: 300px; 
  border: 1px solid red; 
} 
 
video { 
  bottom: 0; 
  width: 100px; 
  border: 1px solid blue; 
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> 
 
<p>Set video source</p> 
<button onclick="setSource(0)">1</button> 
<button onclick="setSource(1)">2</button> 
<button onclick="setSource(2)">3</button> 
<p>video.load() x40</p> 
<button onclick="loadVideo()">load()</button> 
<p> fps: <span id='fps-counter'>0</span></p> 
 
<video autoplay muted loop src='https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4'></video> 
 
<canvas></canvas>

READ ALSO
SQL запрос в Excel на GO, оптимизация

SQL запрос в Excel на GO, оптимизация

Мне нужно сформировать отчет Excel и скачать егоДанные берутся из SQL ( примерно 200 тысяч строк на 70 столбиков)

111
Как обновлять дату

Как обновлять дату

Форма изначально невидима, открывается при нажатии на кнопкуВ форме есть поля, одно из которых input в который нужно вставить дату (каждый раз...

141
JavaScript classList IE9

JavaScript classList IE9

IE9 не поддерживает classList в связи с этим написал небольшую функцию для удаления классов, есть два варианта :

122
Открыть ссылку в новом ОКНЕ браузера

Открыть ссылку в новом ОКНЕ браузера

Реализую виджет "поделиться"

152