简体   繁体   English

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

[英]Not able to dynamically change source of the HTML5 Video with Javascript

Been trying to change the source video of an HTML5 video (after clicking a button).一直在尝试更改 HTML5 视频的源视频(单击按钮后)。 However I run into errors.但是我遇到了错误。 Code and errors below:下面的代码和错误:

Relevant HTML:相关 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>

(regarding the z-index stuff - this video plays then is grabbed by a canvas which re-draws it, as part of the overall application. Probably not relevant but figured I'd mention it.) (关于 z-index 的东西——这个视频播放然后被一个 canvas 抓取,它重新绘制它,作为整个应用程序的一部分。可能不相关,但我想我会提到它。)

Relevant Javascript.相关 Javascript。 Initiates the video, plays it fine.启动视频,播放良好。 It's a little esoteric because I started from someone else's sample.这有点深奥,因为我是从别人的样本开始的。

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;
  
}

So this is fine, my first video exercise_media/vid_exercise_sample_1.mp4 plays just fine.所以这很好,我的第一个视频exercise_media/vid_exercise_sample_1.mp4播放得很好。 However, I want to change the video source to exercise_media/vid_exercise_sample_2.mp4 , same format, located in same folder, etc. (I made sure this video is fine by hard-coding it into the HTML above, and it also plays just fine.)但是,我想将视频源更改为exercise_media/vid_exercise_sample_2.mp4 ,相同的格式,位于相同的文件夹等。(我通过将它硬编码到上面的 HTML 中来确保这个视频很好,它也播放得很好.)

Here's what I've been trying for changing it:这是我一直在尝试更改的内容:

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();
    
}

To no avail, neither ("#video2 > source").src nor ("video2").src work.无济于事, ("#video2 > source").src("video2").src都不起作用。 In the first case, the error is:在第一种情况下,错误是:

Uncaught TypeError: Cannot set property 'src' of null at HTMLButtonElement The video pauses and stays frozen.未捕获的类型错误:无法在 HTMLButtonElement 处设置 null 的属性“src”视频暂停并保持冻结状态。

In the second case, trying video2.src directly (same as document.getElementById("video2") ), the error is在第二种情况下,直接尝试video2.src (与document.getElementById("video2")相同),错误是

Uncaught (in promise) DOMException: Failed to load because no supported source was found. Uncaught (in promise) DOMException: 加载失败,因为没有找到支持的源。

The video portion of the screen goes white/blank.屏幕的视频部分变为白色/空白。

Play/pause functionality works fine, so I know I have a valid reference to my video2 object.播放/暂停功能工作正常,所以我知道我对我的video2 object 有有效的参考。 But I cannot seem to change the source.但我似乎无法改变来源。 And I also know that the other source video works just fine, as I can hard-code it into the HTML and play it without issue.而且我也知道其他源视频也可以正常工作,因为我可以将其硬编码到 HTML 并毫无问题地播放。 But I cannot seem to dynamically switch between them.但我似乎无法在它们之间动态切换。

Any help is much appreciated.任何帮助深表感谢。


Adding more of my code to take a look at.添加更多我的代码来看看。 Don't worry about the pose stuff, it's for my application to analyze the videos, and works fine when I just have one video side by side the webcam (that's what my application does).不用担心姿势的问题,我的应用程序可以分析视频,并且当我只有一个视频与网络摄像头并排时工作正常(这就是我的应用程序所做的)。

The problem is that I cannot change the video2 source to a different MP4.问题是我无法将 video2 源更改为不同的 MP4。 In fact the only way I can have it play an MP4 at all is if I explicitly set it in the HTML.事实上,我可以让它播放 MP4 的唯一方法是,如果我在 HTML 中明确设置它。

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: 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);
    }
}

Now, like I said, if I hard-code into HTML the src of <source> tag, ie <source id="source2" src="exercise_media/vid_exercise_sample_1.mp4" type="video/mp4" > the application runs fine and looks like the following:现在,就像我说的,如果我将<source>标签的src硬编码到 HTML 中,即<source id="source2" src="exercise_media/vid_exercise_sample_1.mp4" type="video/mp4" >应用程序运行良好如下所示:

在此处输入图像描述

(Works fine, the webcam plays in real-time next to the video, and I have my skeletal tracking running on the rightside video as it plays.) (工作正常,网络摄像头在视频旁边实时播放,我的骨骼跟踪在播放时在右侧视频上运行。)

What I want to do is click on eg "Exercise Sample 2" and change the rightside video, obviously.我想要做的是点击例如“Exercise Sample 2”并更改右侧的视频,显然。 Nothing I've tried has worked, such as:我尝试过的任何方法都没有奏效,例如:

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);
       })
    }

}

Specifically for the first line (uncommented), the console says:特别是对于第一行(未注释),控制台说:

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

The right side video turns white and nothing plays.右侧视频变为白色,没有播放任何内容。 If I try instead with video2.setAttribute line uncommented, then the console logs:如果我尝试使用未注释的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

To be clear, I can hard-code in any of the vid_exercise_sample_{2,3,..}.mp4 into the html and they play and run just fine, so I don't believe that's the issue.需要明确的是,我可以将任何vid_exercise_sample_{2,3,..}.mp4硬编码到 html 中,它们可以正常播放和运行,所以我不认为这是问题所在。

Hopefully I've provided a pretty full picture now!希望我现在提供了一个非常完整的图片!

The following example code shows a video .src being updated dynamically.以下示例代码显示了动态更新的视频.src
There are two buttons used to switch between the different sources.有两个按钮用于在不同来源之间切换。

It has been tested on Windows PC using Chrome, Edge and Firefox browsers.它已经在 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>

Hopefully this will give you a reference point towards achieving the quest of "dynamically change source of the HTML5 Video with Javascript" .希望这将为您实现“使用 Javascript 动态更改 HTML5 视频源”的任务提供参考点。 The quest continues...探索还在继续……

Ask anything when you've had chance to test the code.当你有机会测试代码时问任何问题。

Just tried out a quick snippet and it works without a bother.. So weird..刚刚尝试了一个快速的片段,它可以毫不费力地工作..太奇怪了..

what I used我用的

 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>

must be missing something.一定是遗漏了什么。 I'd be able to say for sure if I saw a live example, but that's completely up to you.如果我看到一个活生生的例子,我可以肯定地说,但这完全取决于你。

Otherwise found this on W3否则在 W3 上发现这个

'Warning: Don't make your function asynchronous with the async keyword. '警告:不要使用 async 关键字使您的 function 异步。 You'll lose the "user gesture token" required to allow your video to play later.'您将丢失稍后播放视频所需的“用户手势令牌”。

You're trying to change the source of the <video> element, not the <source> element.. For a quick solution just add an 'id' to that element and change the 'src' property accordingly.您正在尝试更改<video>元素的来源,而不是<source>元素。对于快速解决方案,只需向该元素添加一个“id”并相应地更改“src”属性。

<source id="video2source" src="yoursource.vid">

JS JS

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

Keep in mind, a good practice is to store DOM references in variables.请记住,一个好的做法是将 DOM 引用存储在变量中。 Makes it easier for the browser if you don't perform JS DOM reads all the time.如果您不一直执行 JS DOM 读取,浏览器会更容易。

const vid2src = document.getElementById('video2source'); // define variable in the global scope

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

Hope this helps!希望这可以帮助! =) =)

Edit..编辑..

Question with a similar problem: changing source on html5 video tag有类似问题的问题: 更改 html5 视频标签上的源

Edit numero dos...编辑数量...

Because the play() method is asynchronous, lets try an async solution.因为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);
   })
}

When async is great and all, using it everywhere is not the best idea.. And some methods(like load() and play() are already asynchronous).当 async 很棒时,到处使用它并不是最好的主意。而且有些方法(如load()play()已经是异步的)。

In this fiddle:在这个小提琴中:

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

I used a timer to trigger the change of video source through your setupVideo2 function.我使用定时器通过你的setupVideo2 function 触发视频源的变化。 But as soon as I add await to the function call, it stops working.但是,一旦我将await添加到 function 调用中,它就会停止工作。

So make sure to NOT call your functions related to the video with an await or follow it with .then() .因此,请确保不要使用await调用与视频相关的函数或使用.then()跟随它。 Synchronous code will work just fine and you won't gain anything with the former..同步代码可以正常工作,你不会从前者获得任何东西。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM