读取通过文件输入选择的巨大本地视频文件的最有效方法
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>
下面的代码片段是我正在尝试开发的离线 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>