无法使用Javascript动态更改HTML5视频的源

6

我一直在试图更改HTML5视频的源代码(在点击按钮后)。 但是我遇到了错误。 以下是代码和错误信息:

相关的HTML:

<video id="video2" playsinline controls muted preload="auto" style="z-index: -1; position: absolute;">
    <source src="exercise_media/vid_exercise_sample_1.mp4" type="video/mp4"  > 
</video>

(有关z-index的部分 - 这个视频播放,然后被画布抓取并重新绘制,作为整个应用程序的一部分。可能不相关,但我想提一下。)
相关的Javascript。初始化视频,正常播放。这有点难懂,因为我是从别人的示例开始的。
loadCompanionVideo();

export async function loadCompanionVideo() {
    setStatusText('Setting up companion video...');
  try {
    video2 = await loadVideo2();
  } catch (e) {
    let info = document.getElementById('info');
    info.textContent = '(video player) this device type is not supported yet, ' +
      'or this browser does not support video capture: ' + e.toString();
    info.style.display = 'block';
    throw e;
  }
}

async function loadVideo2() {
  const video2 = await setupVideo2();
  return video2;
}

async function setupVideo2() {
  
  const video2 = document.getElementById('video2');
  
  video2.width = videoWidth2;   // some external numbers
  video2.height = videoHeight2; // just to customize width and height; works fine
  
  return video2;
  
}

这个视频不错,我的第一个视频 exercise_media/vid_exercise_sample_1.mp4 播放得很好。然而,我想将视频源更改为同一文件夹中的 exercise_media/vid_exercise_sample_2.mp4 ,格式相同,等等。(我已经通过硬编码在上面的HTML中测试过此视频,并且它也可以正常播放。)

以下是我尝试更改的内容:

function buttonFunctionChangeVideoSrc() {
    document.getElementById("video2").pause();
    //document.getElementById("#video2 > source").src = "exercise_media/vid_exercise_sample_2.mp4";
    document.getElementById("video2").src = "exercise_media/vid_exercise_sample_2.mp4";
    document.getElementById("video2").load();
    document.getElementById("video2").play();
    
}

徒劳无功,("#video2 > source").src("video2").src都无法工作。在第一种情况下,错误为:

Uncaught TypeError: Cannot set property 'src' of null at HTMLButtonElement 视频暂停并保持冻结状态。

在第二种情况下,尝试直接使用video2.src(与document.getElementById("video2")相同),错误是:

Uncaught (in promise) DOMException: Failed to load because no supported source was found.

视频部分的屏幕变成了白色/空白。

播放/暂停功能正常,因此我知道我有一个有效的对video2对象的引用。但我似乎无法更改源。我也知道另一个源视频完全正常,因为我可以将其硬编码到HTML中并播放它而没有问题。但我似乎无法在它们之间动态切换。

非常感谢任何帮助。


添加更多代码以查看。不要担心姿态问题,这是我的应用程序分析视频所需的,并且当我只有一个同时与网络摄像头并排放置的视频时,它可以正常工作(这就是我的应用程序所做的)。

问题在于我无法将video2源更改为另一个MP4。实际上,我唯一能够播放MP4的方法是在HTML中明确设置它。

HTML:

<div id="canvases" class="canvas-container">
        <div id='main' style='display:none'>
            <video id="video" playsinline style=" -moz-transform: scaleX(-1);
            -o-transform: scaleX(-1);
            -webkit-transform: scaleX(-1);
            transform: scaleX(-1);
            display: none;
            ">
            </video>
            <canvas id="output" class="camera-canvas"></canvas>
            <canvas id="keypoints" class="camera-canvas"></canvas>
            
            <video id="video2" playsinline controls muted style="z-index: -1; position: absolute;" 
            >
            <source id="source2" src="exercise_media/vid_exercise_sample_1.mp4" type="video/mp4"  > 
<!-- hard-coding the src in source is the ONLY way I can play a video here at all...  --> 

            </video>
            
            <canvas id="output2" class="camera-canvas2"></canvas>
            <canvas id="keypoints2" class="camera-canvas2"></canvas>

        <canvas class="illustration-canvas"></cavnas>
        <canvas class="illustration-canvas2"></cavnas>

    </div>

Javascript:

export async function bindPage() {
  setupCanvas();
  setupCanvas2();
  buttonSetup();
  toggleLoadingUI(true);
  setStatusText('Loading AI models...');
  posenet = await posenet_module.load({
    architecture: defaultPoseNetArchitecture,
    outputStride: defaultStride,
    inputResolution: defaultInputResolution,
    multiplier: defaultMultiplier,
    quantBytes: defaultQuantBytes
  });
  setStatusText('Loading FaceMesh model...');
  facemesh = await facemesh_module.load();
  facemesh2 = await facemesh_module.load();
  setStatusText('Loading Avatar file...');
  let t0 = new Date();
  await parseSVG(Object.values(avatarSvgs)[0]);

  setStatusText('Setting up camera...');
  try {
    video = await loadVideo();
  } catch (e) {
    let info = document.getElementById('info');
    info.textContent = '(web cam) this device type is not supported yet, ' +
      'or this browser does not support video capture: ' + e.toString();
    info.style.display = 'block';
    throw e;
  }
  
  try {
    video2 = await loadVideo2();
  } catch (e) {
    console.log("Error loading companion video :: "+e);
  }
  console.log(video2); // shows the full html
  playpauseFunction(); // start video2
  
  toggleLoadingUI(false);
  
  detectPoseInRealTime(video, posenet); //actual application, works fine
  
}

async function loadVideo2() {
  const video2 = await setupVideo2();
  return video2;
}

async function setupVideo2() {
  
  const video2 = document.getElementById('video2');
  
  //video2.setAttribute("src", vid_url_1); //does not work 
  //document.getElementById("source2").src = vid_url_2; // does nothing
  
  videoWidth2orig = video2.videoWidth;
  videoHeight2orig = video2.videoHeight; // gives the actual e.g. 640 x 360
  //.. I do some stuff below to set video2 width/height, works fine
  
  return video2;
  
}

function playpauseFunction() {
    try {
        if (playpause == true) {
            video2.pause();
            playpause = false;
            //alert("Workout paused. Click Play/Pause to resume.");
        } else if (playpause == false) {
            video2.play();
            playpause = true;
        }   
    } catch (e) {
        console.log("playpauseFunction exception :: "+e);
    }
}

现在,像我说的那样,如果我将<source>标签中的src硬编码到HTML中,即<source id="source2" src="exercise_media/vid_exercise_sample_1.mp4" type="video/mp4" >,应用程序能够正常运行并显示如下图所示:

enter image description here

(可以正常工作,网络摄像头实时播放,右侧视频上有我的骨骼跟踪。)

我想做的是,点击“Exercise Sample 2”并更改右侧视频。但我尝试过的一切都没有奏效,例如:

function setupExercise2() {
    
    video2.setAttribute('autoplay', 'true');

    document.getElementById("source2").src = "exercise_media/vid_exercise_sample_2.mp4";
    //video2.setAttribute("src", "exercise_media/vid_exercise_sample_2.mp4"); // have tried both, neither work
    video2.load();

    const playPromise = video2.play()  // trigger video load

    console.log(playPromise); 

    if ( playPromise !== undefined ) {
       playPromise.then(() => {
          // video should start playing as soon as it buffers enough
       }).catch(error => {
          // playback failed, log error
          console.log(error);
       })
    }

}

针对第一行(未注释的行),控制台显示:

Promise{<pending>}__proto__: Promise[[PromiseState]]: "pending"[[PromiseResult]]: undefined

右侧视频变为白色,没有任何播放内容。如果我取消注释video2.setAttribute这一行,那么控制台就会记录如下信息:

Promise {<pending>}
__proto__: Promise
[[PromiseState]]: "rejected"
[[PromiseResult]]: DOMException: Failed to load because no supported source was found.
code: 9
message: "Failed to load because no supported source was found."
name: "NotSupportedError"
__proto__: DOMException

明确一点,我可以在html中硬编码任何一个vid_exercise_sample_{2,3,..}.mp4并且它们都能正常播放和运行,所以我不认为这是问题所在。

希望现在我已经提供了一个相当全面的图景!


2
首先是一个拼写错误: 将 document.getElementById("#video2 > source") 更改为 document.querySelector("#video2 > source")。虽然这应该导致第二个错误,但是这个错误意味着浏览器不支持此URL指向的媒体格式。在开发工具的网络面板中双重检查返回的内容。 - Kaiido
1
你在测试哪个浏览器和操作系统?如果你尝试一个基本的设置(例如:一个视频标签和一个JS函数来改变视频源),会发生什么?通过省略<canvas>和z-index,可以逐步消除某些问题。附注:在答案框中,我将在几分钟内提供一些可测试的基本设置代码。从那里,您可以报告发现的问题。 - VC.One
1
@Kaiido 我听到你的意见,但我打算回答这个问题(如果还需要解决方案)。我无法使用自己的基本代码重现提问者的问题,因此我需要他们也测试相同的代码,并告诉我他们在屏幕上看到了什么问题。然后我可以使用编辑来进行改进。 - VC.One
1
@JDS 看看我的回答是否有帮助。**(1)** 我尝试排除可能性,比如当某些 JS 代码尝试更改一个离屏视频元素的 .src 时可能会发生静默安全阻止问题。这方面没有问题。 (2) 另一个想法是尝试使用完整的 HTTP 路径(不仅仅是 folder/filename.mp4 的设置)。可能就是这么简单的事情。 (3) 尝试我的答案代码,它适用于我的测试,也应该适用于您... - VC.One
1
谢谢,我明天测试并回复您 :) - JDS
显示剩余5条评论
4个回答

3
以下示例代码展示了通过动态更新 .src 来播放视频。有两个按钮用于在不同源之间切换。
此代码已在 Windows PC 上使用 Chrome、Edge 和 Firefox 浏览器进行测试。
<!DOCTYPE html>
<html><head> <meta content="text/html;charset=utf-8" http-equiv="Content-Type"> </head>

<body>

<video id="video2" width="600" height="480" controls style="z-index: 1; overflow:hidden; position: absolute; top: 10px; left: 10px">
<source type="video/mp4">
</video>

<canvas id="myCanvas" width="600" height="480" style="z-index: 1; overflow:hidden; position: absolute; top: 10px; left: 640px">
</canvas>

<div style="z-index: 1; overflow:hidden; position: absolute; top: 510px; left: 10px">
<button onclick="ChangeVideoSrc( 1 )">Play Video 1</button>
<button onclick="ChangeVideoSrc( 2 )">Play Video 2</button>
</div>

<script>

var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');
var timer_DrawCanvas; //# updated later in code

//# Example video links that work on my testing...
vid_url_1 = "https://seed191.bitchute.com/pupNZR0eMW9M/4CU38o2lkgyj.mp4";
vid_url_2 = "https://seed171.bitchute.com/VBdNMrOHylJh/2BMyQPl6BSpZ.mp4";

//# Try your own links if the above works...
//vid_url_1 = "exercise_media/vid_exercise_sample_1.mp4"
//vid_url_2 = "exercise_media/vid_exercise_sample_2.mp4"

//# Set initial starting vid SRC (used on page load)
const myVid = document.getElementById("video2");
myVid.setAttribute("src", vid_url_1);

//# Setup event listeners
myVid.addEventListener("canplay", handle_Media_Events);
myVid.addEventListener("loadeddata", handle_Media_Events);
myVid.addEventListener("play", handle_Media_Events);
myVid.addEventListener("pause", handle_Media_Events);
myVid.addEventListener("ended", handle_Media_Events);

function drawVideo() 
{
    ctx.drawImage(myVid, 0, 0, 600, 480);
}

function ChangeVideoSrc( vidNum ) 
{
    myVid.src = window[ "vid_url_" + vidNum ];
    myVid.load();
}

function handle_Media_Events()
{
    //# if enough data to begin playback
    if ( event.type == "canplay" ) { myVid.play(); }
    
    //# if first frame is available
    if ( event.type == "loadeddata" ) 
    { ctx.drawImage( myVid, 0, 0, 600, 480 ); } //# draws re-sized
    
    if ( event.type == "play" )
    { timer_DrawCanvas = setInterval( drawVideo, 30 ); }
    
    if ( (event.type == "pause") || (event.type == "ended") )
    { clearInterval( timer_DrawCanvas ); }
    
}

</script>

</body>
</html>

希望这能为您实现使用javascript动态更改HTML5视频源的目标提供参考。探索的旅程还在继续...在您有机会测试代码时,随时提问。

2
好的,我注意到以下问题:video2.setAttribute("src", vid_url_1); 不起作用。HTML确实发生了变化(通过console.log检查),但视频不会播放,即当我尝试 video2.play() 时没有任何反应。对于 document.getElementById("source2").src = vid_url_2; 也是同样的结果。这里似乎有些根本性的问题,我将在我的原始帖子中更新更多代码。 - JDS
1
“这里似乎有根本性的问题”是正确的。你能把两个视频链接发送到我的电子邮件 valerio_charles@yahoo.com 吗?我想检查(除其他事项外)浏览器 <video> 标签的 GET 请求是否与 JS 代码尝试的 load() 请求相同。链接的隐私得到保证(无论如何,该网站最终将变为公共网站,因此链接也将显而易见)。 - VC.One

2

刚刚尝试了一个简短的代码片段,它毫无问题地运行了。真是太奇怪了。

我使用的代码:

const v = document.getElementById("vid");
const s = document.getElementById("src");

function changeVideo() { 
   s.src = 'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4';
   v.load();
   v.play();
   console.log(s,v);
}

document.getElementById("change").onclick=changeVideo;
<video id="vid" controls autoplay style="width:75%">
   <source id="src" src="http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4" type="video/mp4">
</video><br/><br/>
<button id="change">Change the video</button>

我觉得可能缺少了一些东西。如果我能看到一个实时的例子,我就能确定了,但这完全取决于你。

另外,在W3上找到了这个:

“警告:不要使用async关键字使您的函数异步化。否则,您将失去允许稍后播放视频所需的“用户手势令牌”。”


1
关于这个答案 - 不幸的是,我正在使用 async 在很多地方工作,而且我并不特别想为了一个简单的功能重写大部分代码库。肯定有一些解决方法。 - JDS
1
这并不是要改变整个应用程序的异步方式,而只是针对loadCompanion视频函数。 - zergski
1
那么我应该如何更改呢?也许您可以参考我在原始问题中编辑/添加的代码。 - JDS
1
你的代码已经可以运行了。我会发布另一个答案并解释一下。 - zergski
1
请告诉我,期待您的回复。 - JDS
@JDS 看一下 :) 希望这个 jsfiddle 有帮助 - zergski

1
你正在尝试更改<video>元素的源,而不是<source>元素。 为了快速解决问题,只需向该元素添加"id"并相应更改"src"属性即可。
<source id="video2source" src="yoursource.vid">

JS

document.getElementById('video2source').src = 'newsource.vid';

记住,一个好的实践是将DOM引用存储在变量中。如果您不一直执行JS DOM读取操作,这将使浏览器更容易。
const vid2src = document.getElementById('video2source'); // define variable in the global scope

vid2src.src = 'newsource.vid'; // use the reference everywhere else

希望这能有所帮助!=)
编辑..
类似问题的提问: 更改html5视频标签上的源 第二次编辑...
由于play()方法是异步的,让我们尝试一种异步解决方案。=)
const video = document.getElementById("video2");
video.setAttribute('autoplay', 'true');  // just in case    

/// ... your previous code ... switch source, and so on and so on

const playPromise = video.play()  // trigger video load

console.log(playPromise); // just check what the console tells you

if ( playPromise !== undefined ) {
   playPromise.then(() => {
      // video should start playing as soon as it buffers enough
   }).catch(error => {
      // playback failed, log error
      console.log(error);
   })
}

1
我相信我已经尝试过这个方法了,通过 document.getElementById("#video2 > source").src = "exercise_media/vid_exercise_sample_2.mp4";。这不是同样的事情吗?我试图改变 source.src - JDS
1
不,JS中的选择器与CSS中的选择器工作方式不同。在这里,您正在查找一个具有“#video2> source”ID的元素。正确的方法是document.getElementById('video2').getElementsByTagName('source')[0].src = 'foo'。请注意,[0]选择HTML集合中的第一个元素,这是使用除getElementById()querySelector()之外的任何其他选择器所给出的内容,它们会获取具有指定选择器的第一个元素。 - zergski
1
没有错误是好的!向正确的方向迈出了一步。 = P 我认为问题很简单,因为您的播放器中没有“autoplay”属性。尝试添加它或在切换源后运行document.getElementById(“video2”)。play()。还可以尝试通过将元素记录到控制台来调试,看看源是否更改。 - zergski
1
很遗憾,我已经添加了 play() 调用,但什么也没有发生。我使用 for(var key in d) { str += key + ': ' + d[key] + '\n' } 来检查源代码并进行整个元素的字符串转储,它确实显示 currentSrc: http://localhost:1234/exercise_media/vid_exercise_sample_2.mp4。但视频就是不播放!如果我再次点击按钮进行测试,我会看到 Uncaught (in promise) DOMException:The play() request was interrupted by a call to pause(). 这对我来说非常重要,所以我会尽快开始提供赏金。感谢你迄今为止的帮助。 - JDS
1
当我点击按钮尝试加载和重新加载两个不同的视频时,我看到另一个错误:Uncaught (in promise) DOMException: The play() request was interrupted by a new load request. - JDS
显示剩余4条评论

0

当异步操作非常棒时,但是在所有地方都使用它并不是最好的主意。有些方法(比如load()play())已经是异步的了。

在这个fiddle中:

https://jsfiddle.net/azdpu3Ly/13/

我使用了一个计时器来触发通过你的setupVideo2函数更改视频源。但是,一旦我在函数调用中添加了await,它就停止工作了。

因此,请确保不要使用await调用与视频相关的函数,也不要在其后跟随.then()。同步代码将正常工作,并且您不会从前者中获得任何好处。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接