简体   繁体   English

如何始终在Android上始终可靠地播放HTML5音频?

[英]How Can I Always Play HTML5 Audio Reliably on Android?

When working with an <audio> element (or <video> element, for that matter) without the controls attribute, mobile devices (both Android and iOS) usually require the user to tap on something in order for the play() call to actually work. 当使用没有controls属性的<audio>元素(或<video>元素)时,移动设备(Android和iOS)通常需要用户点按一下内容,才能对play()进行实际调用工作。 For example, this jQuery code will not work on most mobile devices: 例如,此jQuery代码不适用于大多数移动设备:

$(window).load(function() {
    $('#an_audio_element_without_controls')[0].play();
});

But this will work on most mobile devices: 但是,这在大多数移动设备上工作:

$(window).load(function() {
    $('#some_link').click(function() {
        $('#an_audio_element_without_controls')[0].play();
        return false;
    });
});

After a specific <audio> element has been play() 'd once, it can be play() 'd again without the user having to first tap on something. 在特定的<audio>元素已经被play() 'd一次之后,就可以再次被play() 'd,而用户不必先轻敲某些东西。 So if you were in a situation where you needed to have certain audio files play at certain points of the user's workflow but didn't want them to always have to tap on something for the audio to play, you could have them tap on something at the very beginning and have that initial tap cause an empty <audio> element to play. 因此,如果您处于需要在用户工作流的某些位置播放某些音频文件,但又不想让它们总是必须点击某些东西才能播放音频的情况,则可以让他们在以下位置点击某些东西:刚开始并具有初始抽头会导致播放一个空的<audio>元素。 Then whenever you actually needed to play audio, you could update that specific <audio> element's source and then play it and it would work. 然后,只要您实际需要播放音频,就可以更新该特定<audio>元素的源,然后播放它,它将起作用。

This trick I have just described works well on iOS devices, but on some Android devices, it doesn't always work. 我刚刚描述的这个技巧在iOS设备上效果很好,但在某些Android设备上并不总是有效。 For example, have a look at this code ( jsFiddle demo ): 例如,看下面的代码( jsFiddle demo ):

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Android Audio Test</title>
        <style>
            .column {
                float: left;
                width: 18em;
            }

            .log {
                width: 16em;
                height: 10em;
                padding: 0.5em;
                overflow-y: auto;
                font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
                border: 1px solid #000;
            }
        </style>
    </head>
    <body>
        <div class="column">
            <h1>Dynamic</h1>

            <p>
                This <code>&lt;audio&gt;</code> tag starts out having no
                <code>&lt;source&gt;</code> tags. Those tags get added when the
                user taps on the "Play Audio" link below. Each subsequent tap
                removes the <code>&lt;source&gt;</code> tags and re-adds them
                again. The audio isn't played until the amount of time
                specified in the "Timeout" dropdown menu has passed.
            </p>

            <p>
                Timeout:

                <select id="timeout">
                    <option value="0">None</option>
                    <option value="10">10</option>
                    <option value="20">20</option>
                    <option value="30">30</option>
                    <option value="40">40</option>
                    <option value="50">50</option>
                    <option value="60">60</option>
                    <option value="70">70</option>
                    <option value="80">80</option>
                    <option value="90">90</option>
                    <option value="100">100</option>
                    <option value="150">150</option>
                    <option value="200">200</option>
                    <option value="250">250</option>
                    <option value="300">300</option>
                    <option value="350">350</option>
                    <option value="400">400</option>
                    <option value="450">450</option>
                    <option value="500">500</option>
                </select>
            </p>

            <audio id="audio_test_dynamic" data-log-id="audio_test_dynamic_log"></audio>

            <p><a href="javascript:click_handler_dynamic();">Play Audio</a></p>

            <div class="log" id="audio_test_dynamic_log"></div>
        </div>

        <div class="column">
            <h1>Static</h1>

            <p>
                This <code>&lt;audio&gt;</code> tag's
                <code>&lt;source&gt;</code> tags are hard-coded.
            </p>

            <audio id="audio_test_static" data-log-id="audio_test_static_log">
                <source src="http://www.html5tutorial.info/media/vincent.mp3" type="audio/mpeg">
                <source src="http://www.html5tutorial.info/media/vincent.ogg" type="audio/ogg">
            </audio>

            <p><a href="javascript:click_handler_static();">Play Audio</a></p>

            <div class="log" id="audio_test_static_log"></div>
        </div>

        <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
        <script>
            var events = [
                'abort',
                'addsourcebuffer',
                'canplay',
                'canplaythrough',
                'cuechange',
                'durationchange',
                'emptied',
                'ended',
                'error',
                'loadeddata',
                'loadedmetadata',
                'loadstart',
                'mskeyadded',
                'mskeyerror',
                'mskeymessage',
                'onaddtrack',
                'onchange',
                'onmsneedkey',
                'onremovetrack',
                'pause',
                'play',
                'playing',
                'progress',
                'ratechange',
                'removesourcebuffer',
                'seeked',
                'seeking',
                'sourceclose',
                'sourceended',
                'sourceopen',
                'stalled',
                'suspend',
                'timeupdate',
                'update',
                'updateend',
                'updatestart',
                'volumechange',
                'waiting'
            ];

            $.each(events, function(index, event) {
                $('audio').on(event, function() {
                    $('#' + $(this).data('log-id')).prepend(event + '<br>');
                });
            });

            function click_handler_dynamic() {
                var timeout = parseInt($('#timeout').val(), 10);

                $('#audio_test_dynamic_log').prepend('---timeout set to ' + timeout + '---<hr>');

                $('#audio_test_dynamic').empty().html(
                    '<source src="http://www.html5tutorial.info/media/vincent.mp3" type="audio/mpeg">' +
                    '<source src="http://www.html5tutorial.info/media/vincent.ogg" type="audio/ogg">'
                );

                $('#audio_test_dynamic')[0].load();

                if (timeout) {
                    setTimeout(function() {
                        $('#audio_test_dynamic')[0].play();
                    }, timeout);
                }
                else {
                    $('#audio_test_dynamic')[0].play();
                }
            }

            function click_handler_static() {
                $('#audio_test_static_log').prepend('<hr>');
                $('#audio_test_static')[0].play();
            }
        </script>
    </body>
</html>

I tried the above code on a Motorola Xoom running the stock browser on Android 4.1.2. 我在运行Android 4.1.2的常规浏览器的Motorola Xoom上尝试了上述代码。 I found that if I load (or refresh) the page, leave the "Timeout" dropdown set to "None", and tap the "Dynamic" section's "Play Audio" link, it usually fails to play. 我发现如果加载(或刷新)页面,将“超时”下拉列表设置为“无”,然后点击“动态”部分的“播放音频”链接,则通常无法播放。 I tried the same thing using various timeout options and found that it starts to get fairly reliable at around 200. It gets to be really reliable at around 400. I don't think I have seen 400 fail. 我使用各种超时选项尝试了同样的事情,发现它在200左右开始变得相当可靠。在400左右变得非常可靠。我认为我没有看到400失败。 The thing is, I don't want to find a number that never fails on this Motorola Xoom and then assume that it won't ever fail on any other device. 问题是,我不想在这个Motorola Xoom上找到一个永不失败的数字,然后假设它在任何其他设备上都不会失败。 I also don't want to have to have such a high delay before the audio plays. 我也不想在音频播放之前有这么高的延迟。 It would be nice if I could somehow detect when it's not playing and then force it to try playing again with a limit of five or ten attempts. 如果我能以某种方式检测到它什么时候不播放,然后强制它以五到十次尝试的限制再次尝试播放,那就太好了。 I don't know what I could do to detect a failed play, though. 不过,我不知道该怎么做才能检测出失败的比赛。 The output of a successful play looks very similar to the output of a failed play, at least on the Motorola Xoom. 至少在Motorola Xoom上,成功播放的输出与失败播放的输出非常相似。 Here is the output of a successful play (it's in reverse chronological order): 这是一个成功播放的输出(按倒序排列):

  1. pause 暂停
  2. ended 结束
  3. timeupdate (many times in a row) timeupdate(连续多次)
  4. durationchange durationchange
  5. timeupdate timeupdate
  6. playing 播放
  7. canplaythrough canplaythrough
  8. canplay 可以玩
  9. loadeddata loadeddata
  10. loadedmetadata 等待loadedmetadata
  11. durationchange durationchange
  12. progress 进展
  13. waiting 等候
  14. play
  15. loadstart loadstart
  16. emptied 清空

The only difference between the output of a successful play and the output of a failed play is items 3 and 4 in the above list aren't there (the multiple timeupdate s and the durationchange that happens right before). 成功播放的输出和失败播放的输出之间的唯一区别是,上面列表中的项目3和4不存在(多个timeupdate s和之前发生的durationchange )。 If I tap the "Play Audio" link again after a failed play occurs and it fails again, this is what the output looks like (again, it's in reverse chronological order): 如果在播放失败并再次失败后再次点击“播放音频”链接,则输出结果如下所示(再次,按时间倒序排列):

  1. pause 暂停
  2. playing 播放
  3. canplaythrough canplaythrough
  4. canplay 可以玩
  5. loadeddata loadeddata
  6. loadedmetadata 等待loadedmetadata
  7. durationchange durationchange
  8. progress 进展
  9. waiting 等候
  10. play
  11. loadstart loadstart
  12. emptied 清空
  13. abort 退出

If I tap the link enough times, it will eventually play. 如果我点击链接足够的时间,它将最终播放。

Is there anything I can do to detect when a failed play occurs and have it try to play again? 我有什么办法可以检测出何时播放失败并尝试再次播放?

I have used HTML5 audio and video on Android devices and though I can tell the global experience is more reliable on iOS (due to less fragmentation and sometimes lesser design/horsepower) at some point you will need to rely on the manufacturer implementation of Android (though since Chrome is on Android I have to say things got better). 我已经在Android设备上使用了HTML5音频和视频,尽管我可以说出iOS上的全球体验更加可靠(由于减少了碎片,有时设计/马力也有所降低),但在某些时候,您仍需要依靠Android的制造商实施(尽管Chrome浏览器位于Android上,但我不得不说情况有所改善)。

Normally you can bind to the error, abort or stalled event to detect playback/network issues and act on them (either display an error message or force load(), play()). 通常,您可以绑定到错误,异常终止或停顿事件以检测播放/网络问题并采取措施(显示错误消息或强制load(),play())。 See here for more info . 有关更多信息,请参见此处。

I have seen Android devices took a dozen of seconds between the moment you touch play and the first timeupdate event where an iPad took 3 seconds ... without any error showing up. 我看到Android设备在您触摸播放到第一次timeupdate事件之间花费了十二秒钟,而iPad花费了3秒……没有出现任何错误。

Also I would suggest you bind to the touchstart event rather than the click event for Android touch based device. 我也建议您绑定到touchstart事件,而不是基于Android触摸设备的click事件。

Thanks 谢谢

I have been facing the same issue, when I tried to manage a few videos as a flow in an app I am doing with Cordova.js (webview) I have been experimenting weird behaviour when it comes into play video files through js, with no user interaction. 我一直面临着同样的问题,当我尝试使用Cordova.js(webview)在应用程序中管理一些视频流时,我一直在尝试通过js播放视频文件时出现奇怪的行为,没有用户互动。 Although I was file.load() and then triggering a video.play() when the video triggered a canplaythrough event, the timeupdate of that object shwod me that it stopped at 0 without any further event being triggered. 虽然我file.load(),然后触发video.play()当视频引发了canplaythrough事件,该对象的timeupdate shwod我,它停在0而不触发任何进一步的事件。 Luckily I found this: 幸运的是,我发现了这一点:

http://blog.blairvanderhoof.com/post/78586868260/getting-the-html5-video-tag-to-work-in-cordova-for http://blog.blairvanderhoof.com/post/78586868260/getting-the-html5-video-tag-to-work-in-cordova-for

what pointed me to this 是什么使我指出了这一点

http://developer.android.com/reference/android/webkit/WebSettings.html#setMediaPlaybackRequiresUserGesture(boolean) http://developer.android.com/reference/android/webkit/WebSettings.html#setMediaPlaybackRequiresUserGesture(boolean)

It looks to me like it is definitely what is going on here 在我看来,这肯定是这里正在发生的事情

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

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