HTML5 音频对象无法在 iPad 上播放(从 setTimeout 调用时)

Posted

技术标签:

【中文标题】HTML5 音频对象无法在 iPad 上播放(从 setTimeout 调用时)【英文标题】:HTML5 audio object doesn't play on iPad (when called from a setTimeout) 【发布时间】:2012-06-11 16:05:10 【问题描述】:

我有一个带有隐藏<audio> 对象的页面,该对象通过javascript 使用自定义按钮启动和停止。 (原因是我想自定义按钮,而绘制音频播放器似乎会破坏 iPad 上的渲染性能)。一个简化的例子(在coffeescript中):

// Works fine on all browsers

constructor: (@_button, @_audio) ->
  @_button.on 'click', @_play          // Bind button's click event with jQuery

_play: (e) =>
  @_audio[0].play()                    // Call play() on audio element

从绑定到click 事件的函数触发时,音频播放正常,但我实际上希望在文件播放之前完成动画,所以我将.play() 放在setTimeout 中。但是我无法让它工作:

// Will not play on iPad

constructor: (@_button, @_audio) ->
  @_button.on 'click', @_play          // Bind button's click event with jQuery

_play: (e) =>
  setTimeout (=>                       // Declare a 300ms timeout
    @_audio[0].play()                  // Call play() on audio element
  ), 300

我检查了@_audio (this._audio) 是否在范围内,并且它的play() 方法存在。为什么这在 iPad 上不起作用?

编辑:碰巧的是,上面的简化测试用例实际上确实工作。请参阅下面的@apsillers 的答案以及我的 cmets。

【问题讨论】:

补充一下,我已经在模拟器真正的 iPad 上测试过了。 我想你不能在没有明确的用户操作的情况下在 iPad/iPhone 中播放视频/音频...对于带宽消耗 另外,我在 play 方法周围加了一个try/catch,似乎没有抛出任何错误。 你试过@_button[0].click() 还是trigger()? 【参考方案1】:

见Apple's ios considerations guide:

...JavaScript play()load() 方法在用户开始播放之前也是不活动的,除非用户操作触发了 play()load() 方法。换句话说,用户启动的播放按钮有效,但onLoad="play()" 事件无效。

您的setTimeout() 回调似乎不符合用户启动的操作,尽管setTimeout() 本身在用户启动的函数中。

建议:我没有要测试的 iOS 设备,但也许在用户按下按钮时进行初始播放/暂停会解除此限制。也就是说,您调用 play() 然后立即暂停它,然后调用 animate 和 setTimeout() 函数与 play() 调用。这使得用户启动的功能让 iOS 知道以后可以加载和播放此视频。

【讨论】:

你说得对@apsillers,我所做的不符合“用户发起的操作”。然而,有一个解决方案——我做了更多的挖掘工作,重要的是要指出,当我在我的问题中编写简化的测试用例时我错了——它确实确实有效。请参阅下面的答案了解原因。 我最初的问题的解决方案是继续使用我复杂的多次超时深度动画序列,但为 .play() 方法运行一次超时深度函数,持续时间适当更长.然后它“并行”运行。显然这不是万无一失的,因为动画序列中的计时器可能不会出现,play() 可能会被提前调用。但在我的情况下它似乎工作正常。【参考方案2】:

您无法自动调用播放音频系统的某些设备您是否尝试过使用按钮并单击播放,看看是否有效如果我没记错的话,这与视频标签相同

我认为 safari 会检查触发音频的事件因此为什么单击有效

【讨论】:

【参考方案3】:

@apsillers 在他的回答中建议我可以重新排列我的代码以满足 iPad 的“用户启动操作”要求。我做了一些挖掘,结果证明这是真的。

要求似乎是 play() 调用只能在 one setTimeout 内(因此,我在原始问题中给出的简化示例确实有效——我原来有play()几个setTimeouts深)。

所以这个工作:

constructor: (@_button, @_audio) ->
  @_button.on 'click', @_play

_play: (e) =>
  setTimeout (=>
    @_audio[0].play()                  // play() is only inside one setTimeout
  ), 300

这也可以:

constructor: (@_button, @_audio) ->
  @_button.on 'click', =>
    setTimeout ((e) =>
      @_play(e)
    ), 300

_play: (e) =>
  @_audio[0].play()                    // Still only inside one setTimeout

但这不起作用:

constructor: (@_button, @_audio) ->
  @_button.on 'click', @_play

_play: (e) =>
  setTimeout (=>
    // Something useful
    setTimeout (=>
      @_audio[0].play()                // play() is inside two setTimeouts
    ), 300
  ), 300

这也不会(我原来的设置):

constructor: (@_button, @_audio) ->
  @_button.on 'click', @_play

_play: (e) =>

  @_button
    .animate  prop: value , 300, =>

      setTimeout (=>
        @_audio[0].play()              // play() still 'too deep'
      ), 300

在最后一个示例中,似乎 jQuery 的动画回调是从库内部的另一个 setTimeout 调用的,因此 play() 再次“太深”。

【讨论】:

以上是关于HTML5 音频对象无法在 iPad 上播放(从 setTimeout 调用时)的主要内容,如果未能解决你的问题,请参考以下文章

Ipad 无法在我的网络应用程序上同时播放音频和视频

HTML5 音频“触发播放”在 iPad 上不起作用

html5 视频无法在 iPad 上播放 - 在 iPhone 上播放

在 iPad 上播放音频

如何在 iPad 上使用 HTML5/Javascript 合成音频

无论如何要在 ipad 上自动播放在线内容音频文件