读取通过文件输入选择的巨大本地视频文件的最有效方法

Most efficient way to read huge local video file selected via file input

下面的代码片段是我正在尝试开发的离线 HTML 视频播放器的简化示例。
用户可以选择一个视频文件,然后这个视频文件内容被读入视频播放器,用户可以播放它。

这里的问题是我读取视频文件的方法导致大视频文件大量内存泄漏。
我正在使用 readAsDataURL,这在大文件的情况下是灾难性的(例如,对于 ~200MB 的视频文件,我使用了 ~600MB 的网页内存)。
所以,我的问题是,将用户选择的本地视频文件读入我的 HTML 视频播放器的最有效方法是什么?
我需要一种不会像 readAsDataURL 那样导致内存泄漏的方法。

我认为解决方案的方向可能是将本地视频文件分块,其中只需要切片是 loaded/unloaded to/from 内存,就像在线视频所发生的那样。此外,一种能够直接从本地硬盘读取视频内容而不是先将其作为 DataURL 加载到内存中的方法将会有所帮助。

负责将所选视频文件作为 src:

传递的函数
function videoUpdateSource(file) {
  var reader = new FileReader();
  reader.readAsDataURL(file);

  reader.onloadend = function() {
    var videoTagElement_Exists = document.getElementById("loaded_video");
    !videoTagElement_Exists ? videoElementInitiate() : videoRemoveSources();
    videoAppendNewSource(reader.result);
  }
}

完整测试片段:

function videoElementInitiate() {
  var videoElement = document.createElement('video');
  videoElement.setAttribute('id', 'loaded_video');
  videoElement.setAttribute('width', '480');
  videoElement.setAttribute('height', '300');
  videoElement.setAttribute('controls', '');

  document.getElementById("video_container").appendChild(videoElement);
  videoTag = document.getElementById('loaded_video');
}

function videoUpdateSource(file) {
  var reader = new FileReader();
  reader.readAsDataURL(file);

  reader.onloadend = function() {
    var videoTagElement_Exists = document.getElementById("loaded_video");
    !videoTagElement_Exists ? videoElementInitiate() : videoRemoveSources();
    videoAppendNewSource(reader.result);
  }
}

function videoRemoveSources() {
  videoTag.pause();
  var sourceElements = videoTag.getElementsByTagName('source');
  for (var i = sourceElements.length - 1; i >= 0; --i) {
    sourceElements[i].remove();
  }
  videoTag.removeAttribute('src');
}

function videoAppendNewSource(src) {
  var source = document.createElement('source');
  source.setAttribute('src', src);
  videoTag.appendChild(source);
  videoTag.load();
}
<!DOCTYPE html>
<html>
<head>
  <title></title>

  <meta charset="utf-8">
  <title>Offline Video Player</title>
</head>
<body>
  <form>
    <p>Select Video file in order to play</p>
    <input type="file" id="fileElem" accept="video/*" onchange="videoUpdateSource(this.files[0])">
    <label class="button" for="fileElem">Select video file</label>
  </form>
  
  <br/>
  <div id='video_container'>
    <!-- loaded video element to be placed here -->
  </div>
 </body>
 </html>

感谢 @AKX () 建议使用 URL.createObjectURL 而不是 readAsDataURL 的解决方案。

更新负责将所选视频文件传递为 src:

的函数
function videoUpdateSource(file) {
  var url = URL.createObjectURL(file);     // file is read as blob by default, check side note below.
  var videoTagElement_Exists = document.getElementById("loaded_video");
  !videoTagElement_Exists ? videoElementInitiate() : videoRemoveSources();
  videoAppendNewSource(url);
}

旁注:

A File object is a specific kind of a Blob, and can be used in any context that a Blob can. In particular, FileReader, URL.createObjectURL(), createImageBitmap(), and XMLHttpRequest.send() accept both Blobs and Files.
Source

现在加载 200MB 甚至 1GB 的视频文件会导致约 60MB 的内存使用。

更新的代码片段:

function videoElementInitiate() {
  var videoElement = document.createElement('video');
  videoElement.setAttribute('id', 'loaded_video');
  videoElement.setAttribute('width', '480');
  videoElement.setAttribute('height', '300');
  videoElement.setAttribute('controls', '');

  document.getElementById("video_container").appendChild(videoElement);
  videoTag = document.getElementById('loaded_video');
}

function videoUpdateSource(file) {
  var url = URL.createObjectURL(file); // file is read as blob by default
  var videoTagElement_Exists = document.getElementById("loaded_video");
  !videoTagElement_Exists ? videoElementInitiate() : videoRemoveSources();
  videoAppendNewSource(url);
}

function videoRemoveSources() {
  videoTag.pause();
  var sourceElements = videoTag.getElementsByTagName('source');
  for (var i = sourceElements.length - 1; i >= 0; --i) {
    sourceElements[i].remove();
  }
  videoTag.removeAttribute('src');
}

function videoAppendNewSource(src) {
  var source = document.createElement('source');
  source.setAttribute('src', src);
  videoTag.appendChild(source);
  videoTag.load();
}
<!DOCTYPE html>
<html>
<head>
  <title></title>

  <meta charset="utf-8">
  <title>Offline Video Player</title>
</head>
<body>
  <form>
    <p>Select Video file in order to play</p>
    <input type="file" id="fileElem" accept="video/*" onchange="videoUpdateSource(this.files[0])">
    <label class="button" for="fileElem">Select video file</label>
  </form>
  
  <br/>
  <div id='video_container'>
    <!-- loaded video element to be placed here -->
  </div>
 </body>
 </html>