问题
I've created a little YouTube streaming script which randomly plays YouTube music videos or from a queue. It's worked fine for months up until this week in which it doesn't seem to want to load the videos when using an iPad/iPhone. I get the following error instead:
If playback doesn't begin shortly, try restarting your device
What I've tried:
I've tried Safari, Chrome, Firefox and Opera and they all error. I've tried clearing website data/cache, restarting the device, fulling restarting the device. Nothing works. The weird thing is, it works absolutely fine on a Windows desktop which leads me to believe it's not an error in the code, but something that has either changed with the API or with Safari. My code has not been edited recently either which could have caused it to stop working.
I've tried debugging it using jsconsole.com, nothing suspicious pops up. In some cases, the player will load, the title of the video will display and so will the image, but after about 30 seconds, the error above displays along with the loading spinner.
I'm aware that playVideo(); doesn't work (autoplay) on iOS devices. This is fine, and again, the script has worked previously, i've only had to press play on the first video. But now, it seems like iOS is trying to auto-play the video. So, I've also tested by removing the playVideo() calls, problem still persists.
Tested on iPad 2, iPad mini and iPhone 4 (all with the latest iOS available on the device and all worked previously).
I'm at a loss and trying to get this fixed before the weekend for a house party :) So I'd appreciate any help or hints as to what might be causing the failure on iOS.
Here is the javascript code:
// 2. This code loads the IFrame Player API code asynchronously.
var tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
// 3. This function creates an <iframe> (and YouTube player)
// after the API code downloads.
var player;
function onYouTubeIframeAPIReady() {
player = new YT.Player('player', {
height: '390',
width: '640',
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});
}
// 4. The API will call this function when the video player is ready.
function onPlayerReady(event) {
check_queue_timer = setTimeout(check_queue(),3000);
}
// 5. The API calls this function when the player's state changes.
// The function indicates that when playing a video (state=1),
// the player should play for six seconds and then stop.
var done = false;
function onPlayerStateChange(event) {
if (event.data === 0) {
// TRACK AS DONE, REFRESH QUEUE AND GET NEXT SONG
// POST TO WRITE TO FILE
$.ajax({
type: 'POST',
url: site_url+'ajax_next_track',
dataType: 'json',
data: 'queue_id='+$('#queue_id').val(),
cache: false,
success:
function(data){
if(data.status == 'success'){
$("#queue_table").empty();
if(data.queue.length == 0){
check_queue_timer = setTimeout(check_queue(),3000);
}
$.each(data.queue, function(i, item) {
if(i == 0){
event.target.loadVideoById(item.video_id);
event.target.playVideo();
$('#queue_id').val(item.queue_id);
}
$('#queue_table').append('<tr><td>'+item.title+'</td></tr>');
});
}
else{
console(data.message);
}
},
error:
function( jqXHR, textStatus, errorThrown ){
$('#error_msg').html('Something broke.').fadeIn();
}
});
}
}
function stopVideo() {
player.stopVideo();
}
function check_queue(){
$.ajax({
type: 'POST',
url: site_url+'ajax_next_track',
dataType: 'json',
data: 'no_delete=1',
cache: false,
success:
function(data){
if(data.status == 'success'){
$("#queue_table").empty();
if(data.queue.length == 0){
clearTimeout(check_queue_timer);
check_queue_timer = setTimeout(check_queue(),3000);
}
$.each(data.queue, function(i, item) {
if(i == 0){
player.loadVideoById(item.video_id);
player.playVideo();
$('#queue_id').val(item.queue_id);
clearTimeout(check_queue_timer);
}
$('#queue_table').append('<tr><td>'+item.title+'</td></tr>');
});
}
else{
console(data.message);
}
},
error:
function( jqXHR, textStatus, errorThrown ){
$('#error_msg').html('Something broke.').fadeIn();
}
});
}
UPDATE 1 (25TH NOV 2015)
I've decided to start from scratch and work with the example code on https://developers.google.com/youtube/iframe_api_reference . It seems that loadVideoById() is no longer working on iPads. It was before. If I don't include the loadVideoById() or playVideo() it works. Ideally, i'd like it to work with loadVideoById().
回答1:
I've banged my head against the computer for hours trying to solve this one... Here's what I've found.
This is happening because of Apple's draconian iOS limitation: web pages can only start playing audio/video when responding directly to user feedback. (Note this limitation only affects the first time audio/video is played on a page.)
Usually, to work around Apple's limitation, it's sufficient to make sure your play() call happens directly in an event handler (e.g., a click handler). This works for the Web Audio API and normal HTML5 videos -- but something about the YouTube player prevents this from working for YouTube.
As a result, the solution for YouTube apparently is to require the user to click the YouTube video itself to initiate playback. From then on, you can control it using the YouTube iframe API (play, pause, seek, etc.). But you cannot control the YouTube video using the API until after the user has clicked the video.
For a foolproof user experience, you can hide all of your player-controlling UI and tell users to click the YouTube video itself to start playback. Then, once, the user has clicked the video once, you can activate your UI. Of course, you should design this only to apply to iOS.
Thanks for the headaches, Apple!
回答2:
I had a similar issue and did some digging, turns out Apple have removed the ability to autoplay embed items via scripts etc so the end user doesn't unexpectedly use up their data. The user needs to trigger the play button themselves.
The best way to fix this issue would be check if the user is on mobile or desktop and set a variable (true/false).
This is what I did
var mobileDevice = false;
if(typeof window.orientation !== 'undefined'){
var mobileDevice = true;
}
if(!mobileDevice) ytPlayer.playVideo();
Hopefully that helps.
Source (https://developer.apple.com)
To prevent unsolicited downloads over cellular networks at the user’s expense, embedded media cannot be played automatically in Safari on iOS—the user always initiates playback.
More answers here - YouTube API not working with iPad / iPhone / non-Flash device
回答3:
If the issue is what the other two answers (Adrian & Junior) say then it should be easily solved by accessing the UIWebView
(or WKWebView
) and setting the mediaPlaybackRequiresUserAction
flag. This can be done if you rolled your own solution or if your using the official framework (https://github.com/youtube/youtube-ios-player-helper).
Swift
let playerView = YTPlayerView()
playerView.webView.mediaPlaybackRequiresUserAction = false
回答4:
I faced the problem after installing a DPS audio application to my laptop. The apps modified my existing Audio Driver to their "Speakers (Digital Power Station)" Audio Devices.
By reading a Reddit post, I replace the Audio Device to My Laptop's origin. Thus, the problem fixed for me.
来源:https://stackoverflow.com/questions/33902844/how-to-fix-if-playback-doesnt-begin-shortly-try-restarting-your-device-using