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

Posted

技术标签:

【中文标题】如何在 Android 上始终可靠地播放 HTML5 音频?【英文标题】:How Can I Always Play HTML5 Audio Reliably on Android? 【发布时间】:2014-04-07 18:56:41 【问题描述】:

当使用没有controls 属性的<audio> 元素(或<video> 元素)时,移动设备(androidios)通常需要用户点击某些东西才能获得@ 987654325@致电实际工作。例如,这个 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()'d 一次后,它可以再次play()'d,而无需用户先点击某些东西。因此,如果您处于需要在用户工作流程的某些点播放某些音频文件但不希望他们总是必须点击某些东西才能播放音频的情况,您可以让他们点击某些东西在一开始并具有初始点击会导致播放空的<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 的股票浏览器的摩托罗拉 Xoom 上尝试了上述代码。我发现如果我加载(或刷新)页面,将“超时”下拉菜单设置为“无”,然后点击“动态”部分的“播放音频”链接,通常无法播放。我使用各种超时选项尝试了同样的事情,发现它在 200 左右开始变得相当可靠。它在 400 左右变得非常可靠。我认为我没有看到 400 失败。问题是,我不想在这个摩托罗拉 Xoom 上找到一个永远不会失败的数字,然后假设它在任何其他设备上都不会失败。我也不想在音频播放之前有这么高的延迟。如果我能以某种方式检测到它什么时候没有播放,然后强制它再次尝试播放,限制为五或十次尝试,那就太好了。不过,我不知道我能做些什么来检测失败的游戏。成功播放的输出看起来与失败播放的输出非常相似,至少在摩托罗拉 Xoom 上是如此。这是一场成功的比赛的输出(按时间倒序排列):

    暂停 结束 timeupdate(连续多次) 持续时间变化 时间更新 正在播放 可以通关 可以玩 加载数据 加载元数据 持续时间变化 进展 等待中 播放 加载开始 清空

成功播放的输出和失败播放的输出之间的唯一区别是上面列表中的第 3 项和第 4 项不存在(之前发生的多个 timeupdates 和 durationchange) .如果我在播放失败后再次点击“播放音频”链接并再次失败,这就是输出的样子(同样,它是按时间倒序排列的):

    暂停 正在播放 可以通关 可以玩 加载数据 加载元数据 持续时间变化 进展 等待中 播放 加载开始 清空 中止

如果我点击链接足够多次,它最终会播放。

我可以做些什么来检测播放失败并让它再次尝试播放?

【问题讨论】:

【参考方案1】:

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

通常,您可以绑定到错误、中止或停止事件以检测播放/网络问题并对其采取行动(显示错误消息或强制 load()、play())。更多信息请参见info。

我看到 Android 设备从您触摸 play 到第一次 timeupdate 事件花费了十几秒钟,而 iPad 花费了 3 秒钟......没有出现任何错误。

另外我建议你绑定到 touchstart 事件而不是基于 Android 触摸设备的 click 事件。

谢谢

【讨论】:

感谢您的信息。对于摩托罗拉 Xoom(我也在另一台设备上看到过),音频肯定不会播放,即使等待几分钟。 哼...你试过用简单的音频标签给它一个wav文件吗?您是在 Chrome 还是原生 android 浏览器中尝试?我有点惊讶像 Xoom 这样的设备不会播放任何东西...... 我只指Android的原生/股票浏览器。据我所知,这在 Chrome 中不是问题。请记住,我也在谈论我在问题中描述的具体情况(替换 &lt;audio&gt; 元素的源然后尝试播放它)。按原样播放&lt;audio&gt; 元素(不事先弄乱它的源)工作正常。【参考方案2】:

我一直面临同样的问题,当我尝试在使用 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)

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

【讨论】:

以上是关于如何在 Android 上始终可靠地播放 HTML5 音频?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 iPhone 上可靠地获取位置

如何使用 audio_service 在 Android 上显示当前正在播放的音频?

如何在 Android 上播放非正弦音符?迷笛?

如何强制网页在 iPhone 和 Android 上始终以 1.0(不是 1.5)的像素比呈现?

soundcloud 小部件:自动播放在 Firefox 16 中无法始终如一地工作

在 CSS 中隐藏原生 Android HTML5 视频播放按钮