无法覆盖HTMLMediaElement.prototype.play

时间:2019-06-26 15:52:39

标签: javascript playback

我在工作时制作了一个复杂的视频播放器。 我发现自己处于以下情况:我有一个视频标签(ours),通常用于播放任何类型的视频(我的和Ads引擎的视频)。 有时,广告引擎(加载外部脚本以显示广告)会在我们使用的视频标签上设置一个新的视频标签(theirs)(这样我可以同时拥有两个<video>标签)。

DOM的情况如下:

<div id="content">
    <div id="ads">
        <video ...></video>
        <iframe ... ></iframe>
    </div>
    <video ...></video>
</div>

我们的视频标签已在其实例上应用了“ hack”,以重试播放音量静音或失败的内容(由于最近的浏览器自动播放音频政策)。

实际上,无法通过广告引擎来管理video tag,我们甚至不应该选择它,因为我们不知道它是否存在或排序(可能是最后一次机会)

有时,their个视频标签中断了ours的整个流程,因为它由于浏览器政策而无法播放,并且我们无法得知它是否(正确地)实施了相同(或类似)的骇客行为我们用来使播放器静音,因此它会阻塞并且ours内容无法播放。

因此,我们的想法是无条件地在HTMLMediaElement.prototype.play上应用该骇客(或其他版本)。

由于我们的“黑客”内部有一些指令会根据本地情况运行(不应暴露于their视频标签,如下所示),因此我们尝试创建一个“黑客” ours(在实例上),theirs(+ ours,但被替换):


const originalPlay = HTMLMediaElement.prototype.play;

// -------- PROTOTYPE OVERRIDE ATTEMPT -----------


HTMLVideoElement.prototype.play = function() {
  console.log("Using ours play method.", this);
  const promise = originalPlay.call(this);

  if (!promise) {
    return Promise.reject();
  }

  return promise
    .catch((reason: DOMException) => {
      if (!(reason && (reason.name === "AbortError" || reason.name === "NotAllowedError") && this.volume > 0)) {
        return Promise.reject(reason);
      }

      this.volume = 0;
      this.muted = true;

      // Original play should not fail with volume 0;
         return originalPlay.call(this);
      });
   }


// -------- OURS VIDEO TAG INSTANCE -----------

this.videoNode.play = () => {
    console.log("Calling internal play")
    const promise = originalPlay.call(this.videoNode);

    if (promise) {
      return promise
        .then(() => {
          if (this.videoNode.volume !== 0) {
            this.props.setAudioPlayRightAcquired();
          }
        })
        .catch((reason: DOMException) => {
          if (this.props.didAcquireAudioPlayRight() || !(reason && (reason.name === "AbortError" || reason.name === "NotAllowedError") && this.videoNode.volume > 0)) {
            return Promise.reject(reason);
          }

          const currentVolume = this.videoNode.volume;

          if (!this.props.contentStarted) {
            this.props.onVolumeChange(0);
          }

          // Original play should not fail with volume 0;
          return originalPlay.call(this.videoNode)
            .catch(() => {
              // The video failed again with (probably) muted volume
              // So we are recovering the volume (it doesn't make sense to keep
              // it muted)

              if (currentVolume !== this.videoNode.volume) {
                // If we changed the volume to try again
                this.props.onVolumeChange(currentVolume);
              }

              return Promise.reject(reason);
            });
        });
    }

    return Promise.resolve();
}

ours hack可以正常工作,但是原型的行为却很奇怪。 如果我尝试console.log的结果,那就是它给出的结果:

console.log(originalPlay); // ƒ play() { [ native code ] }
console.log(HTMLMediaElement.prototype.play); // ƒ play() { ... my code ... }

但是,如果我尝试选择theirs视频标签:

document.querySelectorAll("video")[0].play; // ƒ play() { [ native code ] }

如果我尝试禁用ours骇客并选择ours ??视频标记:

document.querySelectorAll("video")[1].play // ƒ play() { ... my code ... }

如果不禁用它,我会在ours视频标记上应用ours骇客。

所以我不明白为什么新的<video>标签没有使用此方法? 奇怪的是,如果我尝试停止theirs视频并更改DOM并添加一个新元素(因此我创建了一个新的视频实例),则对其应用了hack,并且似乎可以正常工作:实际上,它可以成功打印出我插入的console.log

我无法解释自己的可能性,因为我在创建their视频标签之前创建了“ hack”(我确信这一点)。

有人有任何线索吗?谢谢。

0 个答案:

没有答案