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

时间:2014-04-07 18:56:42

标签: javascript android html5-audio

使用<audio>元素(或<video>元素)时,如果没有controls属性,移动设备(Android和iOS)通常需要用户点按为play()调用实际工作而做的事情。例如,这个jQuery代码将在大多数移动设备上运行:

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

但是这个在大多数移动设备上运行:

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

特定<audio>元素已经play()一次后,它可以再次play(),而用户不必先点击某些内容。因此,如果您需要在用户工作流程的某些位置播放某些音频文件,但又不希望他们总是需要点击某些内容才能播放音频,那么您可以让他们在一开始就点击某些东西并让初始点击导致空<audio>元素播放。然后,无论何时您真的需要播放音频,您都可以更新该特定<audio>元素的来源,然后播放它,它就会起作用。

我刚才描述的这个技巧在iOS设备上运行良好,但在某些Android设备上,它并不总是有效。例如,看一下这段代码(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>

我在运行Android 4.1.2上的股票浏览器的Motorola Xoom上尝试了上述代码。我发现如果我加载(或刷新)页面,请退出&#34; Timeout&#34;下拉菜单设置为&#34;无&#34;,然后点按&#34;动态&#34;部分&#34;播放音频&#34;链接,它通常无法播放。我尝试使用各种超时选项同样的事情,并发现它在200左右开始相当可靠。它在400左右变得非常可靠。我不认为我看到400失败。问题是,我不想在这个摩托罗拉Xoom上找到一个永不失败的数字,然后假设它在任何其他设备上都不会失败。我也不想在播放音频之前有这么高的延迟。如果我能够以某种方式检测它何时没有播放然后强制它再次尝试再次播放,那将是一件好事。但是,我不知道如何才能发现失败的游戏。成功游戏的输出看起来非常类似于失败游戏的输出,至少在摩托罗拉Xoom上。以下是成功游戏的输出(按逆时间顺序排列):

  1. 暂停
  2. 结束
  3. timeupdate(连续多次)
  4. durationchange
  5. timeupdate
  6. canplaythrough
  7. canplay
  8. loadeddata
  9. 等待loadedmetadata
  10. durationchange
  11. 进步
  12. 等待
  13. 播放
  14. loadstart
  15. 成功比赛的输出和失败比赛的输出之间的唯一区别是上面列表中的第3项和第4项都没有(多个timeupdate和{{1}之前发生的事情)。如果我点击播放音频&#34;在播放失败后再次链接并再次失败,这就是输出的样子(再次,它按时间倒序排列):

    1. 暂停
    2. canplaythrough
    3. canplay
    4. loadeddata
    5. 等待loadedmetadata
    6. durationchange
    7. 进步
    8. 等待
    9. 播放
    10. loadstart
    11. 中止
    12. 如果我点击链接足够多次,它最终会播放。

      我可以做些什么来检测失败的比赛何时发生并尝试再次发挥?

2 个答案:

答案 0 :(得分:1)

我在Android设备上使用过HTML5音频和视频,虽然我可以说iOS上的全球体验更可靠(由于碎片更少,有时设计/马力更小),您需要依赖制造商实施Android(虽然Chrome在Android上我不得不说事情变得更好了。)

通常,您可以绑定错误,中止或停止事件以检测回放/网络问题并对其进行操作(显示错误消息或强制加载(),播放())。请点击此处查看更多info

我看到Android设备在您触摸播放的那一刻和第一次更新时间之间花费了十几秒钟,其中iPad耗时3秒......没有出现任何错误。

此外,我建议您绑定到touchstart事件,而不是基于Android touch设备的点击事件。

谢谢

答案 1 :(得分:0)

我一直面临同样的问题,当我尝试管理一些视频作为我在使用Cordova.js(webview)的应用程序中的流程时,我一直在通过js播放视频文件时尝试奇怪的行为,没有用户交互。虽然我 file.load(),然后在视频触发 canplaythrough 事件时触发 video.play()该对象的timeupdate 告诉我它停在 0 而没有任何进一步的事件被触发。幸运的是我发现了这个:

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

是什么让我想到了这个

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

在我看来,这绝对是在这里发生的事情