For some odd reason the HTML5 Audio API won't give me access to currentTime in iOS 4 and 5 for iPads and iPods. I need to adjust currentTime whenever an audio element is played, but it acts like the current time isn't accessible. Looked everywhere for an answer on this and I can't figure it out. My code below works fine in all modern browsers, so I'm a bit stumped.
My HTML markup
<p>
<a class="audio" href="assets/audio/ambient-sounds-1.mp3" data-flash="http://athens.sierrabravo.net/~ablue/plato/videoplayer/assets/audio/niftyplayer.swf" data-mp3="http://athens.sierrabravo.net/~ablue/plato/videoplayer/assets/audio/ambient-sounds-1.mp3" data-ogg="http://athens.sierrabravo.net/~ablue/plato/videoplayer/assets/audio/ambient-sounds-1.ogg">
Ambient Sounds
<span class="audio-play">Play</span>
<audio preload="">
<source preload="" src="http://athens.sierrabravo.net/~ablue/plato/videoplayer/assets/audio/ambient-sounds-1.mp3" type="audio/mpeg">
<source preload="" src="http://athens.sierrabravo.net/~ablue/plato/videoplayer/assets/audio/ambient-sounds-1.ogg" type="audio/ogg">
</audio>
</a>
</p>
<p>
<a class="audio" href="assets/audio/city_street.mp3" data-flash="http://athens.sierrabravo.net/~ablue/plato/videoplayer/assets/audio/niftyplayer.swf" data-mp3="http://athens.sierrabravo.net/~ablue/plato/videoplayer/assets/audio/city_street.mp3" data-ogg="http://athens.sierrabravo.net/~ablue/plato/videoplayer/assets/audio/city_street.ogg">
City Street Sounds
<span class="audio-play">Play</span>
<audio preload="">
<source preload="" src="http://athens.sierrabravo.net/~ablue/plato/videoplayer/assets/audio/city_street.mp3" type="audio/mpeg">
<source preload="" src="http://athens.sierrabravo.net/~ablue/plato/videoplayer/assets/audio/city_street.ogg" type="audio/ogg">
</audio>
</a>
</p>
<p>
<a class="audio" href="#" data-flash="http://athens.sierrabravo.net/~ablue/plato/videoplayer/assets/audio/niftyplayer.swf" data-mp3="http://athens.sierrabravo.net/~ablue/plato/videoplayer/assets/audio/crash.mp3" data-ogg="http://athens.sierrabravo.net/~ablue/plato/videoplayer/assets/audio/crash.ogg">
Loud Crash
<span class="audio-play">Play</span>
<audio preload="">
<source preload="" src="http://athens.sierrabravo.net/~ablue/plato/videoplayer/assets/audio/crash.mp3" type="audio/mpeg">
<source preload="" src="http://athens.sierrabravo.net/~ablue/plato/videoplayer/assets/audio/crash.ogg" type="audio/ogg">
</audio>
</a>
</p>
My JavaScript
AudioPlayer = {
init: function () {
var player = this;
this.audio = $('.audio'); // Save and cache all audio elements so they can be targeted by mass stop()
player.audio.each( function() {
// Bind each audio element
player.bind($(this));
});
},
bind: function (object) {
var player = this;
object.bind('touchstart click', function() {
// get the real DOM element, not the jQuery array
var audio = $(this).find('audio').get(0);
if(audio.paused) { // Check if its playing
player.play(audio);
}
else {
player.stop(audio);
}
return false;
});
},
play: function(audio) {
// Stop all existing audio elements and set them to 0
this.audio.each( function() {
var audioOther = $(this).find('audio').get(0);
audioOther.pause();
});
// Play the audio element
if(audio.currentTime) audio.currentTime = 0;
audio.play();
},
stop: function(audio) {
if(audio.currentTime) audio.currentTime = 0;
audio.play();
}
};
There is an answer as to why my code doesn't work, iOS 4 and 5 need the entire audio element to be completely destroyed (terrible solution, but it works). Here is a quick rundown on how to fix my issue if you've run into it.
Insert only 1 element into the page (I inserted mine right before the closing tag).
On click to play a new audio element, completely destroy the existing and rebuild it from scratch with the new track you want to play.
Call .play on the audio element and you'll be good to go.
I also have the same problem and googled then found this page and the below page
http://remysharp.com/2010/12/23/audio-sprites/
this guy probably found the more detailed answer.
I had these types of problems, and I too started using that desperate way to solve the issue which consists of recreating the audio component, reloading the source, etcetera.
A day or two ago I found that the source of the media content must be "pure" to be able to use it successfully from the ipad.
I want to express with "pure" all content that is not being "transmuted" by previous code that could change or delete it's metadata before it reaches the ipad client.
In the current project I am working on, I found that with Ipad I could not replay nor traverse to mm:ss any content located in the "documents" folder, but if I got that file and moved elsewhere, it worked perfectly, I could replay it on my ipad, go to the minute:second I wished, etcetera.
In my case, an .htaccess was forcing my apache server to send the "get" query through a "download.php" which was checking permissions and doing weird stuff of the like, and then this download.php was dispatching the file to the client, but not in a good way or a way more of the ipad's liking.
I removed the .htaccess bindings and the ipad got a nice audio handling again. (That download.php was of not too much good use anyway...)
Related
So I have a video that I need to autoplay. It works pretty well on Android(Chrome & FF) but iOS Safari does not seem to want to autoplay the video. Here is the code I am using (its a twig template) -
<video class="home-video" poster="{{paths.files}}{{record.templatefields.video_poster}}"
autoplay loop muted controls >
<source src="{{paths.files}}{{record.templatefields.video}}" type="video/mp4" />
<source src="{{paths.files}}{{record.templatefields.video_ogg}}" type="video/ogg" />
</video>
I have already read https://webkit.org/blog/6784/new-video-policies-for-ios
I have ensure the video does not have any audio track so it should not be a problem. The device is using iOS 10+
Any clues would be greatly appreciated..
Add the 'playsinline' attribute. Also ensure no other elements are on top of it.
Add playsinline attribute.
If your video is out of viewport or behind other elements, use javascript play() shortly after pageload, this may help (as long as other needed attributes are set).
Using jQuery (extracted from my bigger jQuery plugin):
var start_video = function($target){
var video_width = $target.width();
var video_height = $target.height();
// if not loaded yet, wait another 500ms
if ($target.get(0).readyState < 2 || video_width == 0 || video_height == 0){
setTimeout(function(){
start_video($target);
}, 500);
return;
}
$target.get(0).play();
}
$(document).ready(function() {
start_video($('.home-video'));
}
I apologize up front as I'm very confused by my problem. I'm really in a bind because this is causing a problem on my production site.
I have a javascript player on my site which plays through song lists which can be hosted on youtube, soundcloud or vimeo. Yesterday I noticed this error which generally arises anytime you try to load a new song through "skipping" with the player buttons. This error just started in the last day or two. I am not seeing anything new in the youtube api release notes and this error occurs using Chrome, Firefox and Safari, so it is most likely not related to a change in the browser. Something that I am using has changed though, as I have not pushed new code in 18 days.
An example playlist is here: http://www.muusical.com/playlists/programming-music
I think I have isolated the way to reproduce the error, here are the steps:
Play a youtube hosted song.
Skip to any other song in the list (whether by pressing the skip buttons or directly pressing the play button on the song's row item).
*Note, that if the first song in the playlist is a youtube song, simply skipping to another song even without playing the initially loaded youtube song will produce the error.
Essentially, the error seems to occur once you have loaded and/or played a youtube song and attempt to skip to another song.
Let me know if you find an exception to this behavior.
I see this error in the console:
Failed to execute 'postMessage' on 'DOMWindow': The target origin provided ('https://www.muusical.com') does not match the recipient window's origin ('http://www.muusical.com').
I load the player with using the youtube javascript api:
new YT.Player('playlist-player', {
playerVars: { 'autoplay': 1, 'fs': 0 },
videoId: "gJ6APKIjFQY",
events: {
'onReady': #initPlayerControls,
'onStateChange': #onPlayerStateChange
}
})
Which produces this iframe:
<iframe id="playlist-player" frameborder="0" allowfullscreen="1" title="YouTube video player" width="640" height="360" src="https://www.youtube.com/embed/gJ6APKIjFQY?autoplay=1&enablejsapi=1&origin=http%3A%2F%2Fwww.muusical.com"></iframe>
After hitting skip from the above youtube song, this is what I see loaded in the iframe:
<iframe id="playlist-player" frameborder="0" allowfullscreen="1" title="YouTube video player" width="640" height="360" src=""></iframe>
I support youtube, soundcloud and vimeo songs. It seems like once a youtube song is loaded the "origin" changes from http to https. I don't think it is necessary to include the embedding methods for the other hosts as this error occurs even if the entire playlist is only youtube and it does not occur in a playlist which consists of only songs from soundcloud and vimeo.
Also, this is how I am loading the youtube javascript:
// Load the IFrame Player API code asynchronously.
var tag = document.createElement('script');
tag.src = "https://www.youtube.com/player_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
Please let me know if you need me to clarify anything and thanks in advance for taking a look.
I have read a bit about this, some SO posts here and there and this link too : https://code.google.com/p/gdata-issues/issues/detail?id=5788
I was about to add a comment to your question, saying I was getting crazy over this ... but when I started to describe my setup, I found a way to avoid the issue ^^.
I start with an empty div element, and use the Youtube Iframe API to turn it into an iframe with all the necessary options.
I have multiple divs like this one, and usually use the same JS variable to store all those players, but one at a time (one replaces the other, and so on ... - it could be better, I know).
To fix the issue, I had the idea to destroy the player with youtubePlayer.destroy(); before building a new one from another element. No more JS errors bleeding in my Chrome console :).
Hope it helps, all the litterature I could read about http and https did not apply to my case, because I am not setting the iframe URL myself, and my website happens to be not https ...
I did restore an async call instead of a static script tag in my HTML but I do not think this was necessary.
EDIT : this error message is quite misleading in fact, it only vaguely means : you are not using the youtube API the proper way :)
#sodawillow's answer is partially correct but I'd like to give the details to the solution and what caused my code to stop calling the .destroy() method to remove the youtube player.
My site has a player which swaps out songs from various sites, one of them being Youtube. There can be different methods for removing a player depending on the type it is. My code checks for the existence of a youtube player and if passes the check then it uses the .destroy() method which only the youtube player has. The problem is that YouTube changed the names of some of the static variables on their player object. For example, if I created a player via:
var player = new YT.Player('playlist-player', {
playerVars: { 'autoplay': 1, 'fs': 0 },
videoId: "gJ6APKIjFQY",
events: {
}
})
then there would be a variable player.L which held the string "player". So to check if the current player was a YouTube player and remove it I did this:
if (player.L == "player") {
player.destroy();
} else {
//handle the removal of the Soundcloud or Vimeo player.
}
Sometime recently Youtube changed the location of the string "player" to now reside at player.M. I could change the above code to check player.M instead of player.L and that would work but to try to avoid this issue in the future I instead have implemented:
if (player.destroy) {
player.destroy();
} else {
//handle the removal of the Soundcloud or Vimeo player.
}
As long as Youtube does not remove the .destroy() method unannounced this will not cause any issues.
So in summary, the issue was as #sodawillow guessed, I was not using the .destroy() method to remove the Youtube player. The reason was because Youtube made some unannounced changes to their api, changing the location of some of the static variables.
This error belongs to the Google Youtube API.
Inside "https://www.youtube.com/iframe_api":
if (!window['YT']) {
var YT = {loading: 0, loaded: 0};
}
if (!window['YTConfig']) {
var YTConfig = {'host': 'http://www.youtube.com'};
}
They use http instead of https.
We need to override the 'host' option and 'widget_referrer' same as 'origin'.
player = new YT.Player('player', {
host: 'https://www.youtube.com',
height: '390',
width: '640',
videoId: 'M7lc1UVf-VE'
...
origin: "https://www.enziin.com",
widget_referrer: "https://www.enziin.com"
}
Goodluck.
My solution is to write all the Youtube player logic in a separate page (as blank as posible) , and that page be referenced in an IFRAME tag.
<iframe src="/youtube_frame/?data=SOME_DATA -%>" height="400px" width="100%" frameborder="0" border="0" scrolling="no"></iframe>
Then, your youtube_frame.html will be something like this:
<!DOCTYPE html>
<html>
<body>
<!-- 1. The <iframe> (and video player) will replace this <div> tag. -->
<div id="player"></div>
<script>
// 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',
videoId: 'M7lc1UVf-VE',
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});
}
// 4. The API will call this function when the video player is ready.
function onPlayerReady(event) {
// event.target.playVideo();
}
// 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) {
console.log("OnplayerStateChange");
if (event.data == YT.PlayerState.PLAYING && !done) {
console.log("OnplayerStateChange - If statement");
setTimeout(stopVideo, 6000);
done = true;
}
}
function stopVideo() {
player.stopVideo();
}
</script>
</body>
</html>
(As a marginal note: my context is that Prototype.js interferes with the event 'OnStateChange', and we cannot remove this dependency.
Using a jsfiddle would not be useful to reproduce the issue, as it has few dependencies.
My app here is built in Rails, and using this plugin for showing a Video playlist: https://github.com/Giorgio003/Youtube-TV/)
I'm having this one my page
<video>
<source type="video/mp4; codecs='avc1.4d0028, mp4a.40.5';" src="xxxxx.m3u8">
</source>
</video>
since I'm using m3u8, the closed captioning button will automatically show up by default.
Below link is exactly the issue
https://developer.apple.com/library/ios/qa/qa1801/_index.html
and according to this:
iOS 7 also supports a CLOSED-CAPTIONS attribute for the EXT-X-STREAM-INF tag. You can declare the absence of closed caption content in the playlist by specifying CLOSED-CAPTIONS=NONE on the EXT-X-STREAM-INF tag. The "Unknown CC" option as shown in Figure 1 will not be displayed in the selection when the absence of closed caption content is declared.
I need to set CLOSED-CAPTIONS to none, but the problem is I'm not sure where to set it from my html, I've tried something like
<video>
<source type="video/mp4; codecs='avc1.4d0028, mp4a.40.5'; closed-captions=none" src="xxxxx.m3u8">
</source>
</video>
didn't work though.
Any thoughts?
using Javascript you can control the captions
in the sample below I'm using jQuery (because my page was already using it) but you can do the same without that. I also target a specific ID for my <video> element, imaginatively called "video", and I also include controls on there to pause/seek the video <video id="video" controls>.
<script type="text/javascript">
$(document).ready(function() {
var video = document.querySelector('#video'); // get the video element
var tracks = video.textTracks; // one for each track element
var track = tracks[0]; // corresponds to the first track element
track.mode = 'hidden';
});
I am using popcornjs to load an interact with a video from youtube.
When I use the code from the documentation:
<html>
<head>
<script src="http://popcornjs.org/code/dist/popcorn-complete.min.js"></script>
<script>
// ensure the web page (DOM) has loaded
document.addEventListener("DOMContentLoaded", function () {
// Create a popcorn instance by calling the Youtube player plugin
var example = Popcorn.youtube(
'#video',
'http://www.youtube.com/watch?v=CxvgCLgwdNk' );
// add a footnote at 2 seconds, and remove it at 6 seconds
example.footnote({
start: 2,
end: 6,
text: "Pop!",
target: "footnotediv"
});
// play the video right away
//example.play(); => commented because you can't autoplay on ios
}, false);
</script>
</head>
<body>
<div id="video" style="width: 360px; height: 300px;" ></div>
<div id="footnotediv"></div>
</body>
</html>
It looks perfect on any browser, but nothing shows on the iPad.
When I load a video with popcorn but without using Youtube, it seems to work fine.
What can I do?
Unfortunately, the ipad doesn't support Flash. We do have a ticket in progress with Popcorn to switch to their HTML5 API, which you can view here:
https://webmademovies.lighthouseapp.com/projects/63272/tickets/329-support-youtube-html5-api-playback-engine
Hope that helps,
Brett
Try using the smart media wrapper:
var example = Popcorn.smart(
'#video',
'http://www.youtube.com/watch?v=CxvgCLgwdNk' );
// add a footnote at 2 seconds, and remove it at 6 seconds
example.footnote({
start: 2,
end: 6,
text: "Pop!",
target: "footnotediv"
});
// play the video right away
//example.play(); => commented because you can't autoplay on ios
}, false);
I too am facing the issue, Youtube Popcorn is not working in iPad. But interestingly I found a site using popcorn youtube and it works fine in iPad
Link:
http://www.retn.org/show/south-burlington-school-board-meeting-october-2-2013
So, I guess someone should come up with more specific answer
Edit this file in your code:
https://github.com/mozilla/popcorn-js/blob/master/wrappers/youtube/popcorn.HTMLYouTubeVideoElement.js
Comment body part of onPlayerReady() on line no 117 and add following statements in this function in case of iPad.
addYouTubeEvent("play", onFirstPlay);
playerReady = true;
mediaReady = true;
player.mute();
Reason: Youtube on iPad wants user interaction, you can't start it programmatically and due to player.isMuted() method returns false in case of iPad, addYouTubeEvent("play", onFirstPlay) statement is never called.
We are creating an HTML5 website for mobile and need to get camera access through the web browser without being a native app. We are having trouble making this work in iOS. Is anyone aware of a solution for this?
You could try this:
<input type="file" capture="camera" accept="image/*" id="cameraInput" name="cameraInput">
but it has to be iOS 6+ to work. That will give you a nice dialogue for you to choose either to take a picture or to upload one from your album i.e.
An example can be found here:
Capturing camera/picture data without PhoneGap
As of 2015, it now just works.
<input type="file">
This will ask user for the upload of any file. On iOS 8.x this can be a camera video, camera photo, or a photo/video from Photo Library.
<input type="file" accept="image/*">
This is as above, but limits the uploads to photos only from camera or library, no videos.
In iOS6, Apple supports this via the <input type="file"> tag. I couldn't find a useful link in Apple's developer documentation, but there's an example here.
It looks like overlays and more advanced functionality is not yet available, but this should work for a lot of use cases.
EDIT: The w3c has a spec that iOS6 Safari seems to implement a subset of. The capture attribute is notably missing.
I think this one is working.
Recording a video or audio;
<input type="file" accept="video/*;capture=camcorder">
<input type="file" accept="audio/*;capture=microphone">
or (new method)
<device type="media" onchange="update(this.data)"></device>
<video autoplay></video>
<script>
function update(stream) {
document.querySelector('video').src = stream.url;
}
</script>
If it is not, probably will work on ios6, more detail can be found at get user media
Update 11/2020: The Google Developer link is (currently) dead. The original article with a LOT more explanations can still be found at web.archive.org.
This question is already a few years old but in that time some additional possibilities have evolved, like accessing the camera directly, displaying a preview and capturing snapshots (e.g. for QR code scanning).
This Google Developers article provides an in-depth explaination of all (?) the ways how to get image/camera data into a web application, from "work everywhere" (even in desktop browsers) to "work only on modern, up-to-date mobile devices with camera". Along with many useful tips.
Explained methods:
Ask for a URL: Easiest but least satisfying.
File input (covered by most other posts here): The data can then be attached to a or manipulated with JavaScript by listening for an onchange event on the input element and then reading the files property of the event target.
<input type="file" accept="image/*" id="file-input">
<script>
const fileInput = document.getElementById('file-input');
fileInput.addEventListener('change', (e) => doSomethingWithFiles(e.target.files));
</script>
The files property is a FileList object.
Drag and drop (useful for desktop browsers):
<div id="target">You can drag an image file here</div>
<script>
const target = document.getElementById('target');
target.addEventListener('drop', (e) => {
e.stopPropagation();
e.preventDefault();
doSomethingWithFiles(e.dataTransfer.files);
});
target.addEventListener('dragover', (e) => {
e.stopPropagation();
e.preventDefault();
e.dataTransfer.dropEffect = 'copy';
});
</script>
You can get a FileList object from the dataTransfer.files property of the drop event.
Paste from clipboard
<textarea id="target">Paste an image here</textarea>
<script>
const target = document.getElementById('target');
target.addEventListener('paste', (e) => {
e.preventDefault();
doSomethingWithFiles(e.clipboardData.files);
});
</script>
e.clipboardData.files is a FileList object again.
Access the camera interactively (necessary if application needs to give instant feedback on what it "sees", like QR codes): Detect camera support with const supported = 'mediaDevices' in navigator; and prompt the user for consent. Then show a realtime preview and copy snapshots to a canvas.
<video id="player" controls autoplay></video>
<button id="capture">Capture</button>
<canvas id="canvas" width=320 height=240></canvas>
<script>
const player = document.getElementById('player');
const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');
const captureButton = document.getElementById('capture');
const constraints = {
video: true,
};
captureButton.addEventListener('click', () => {
// Draw the video frame to the canvas.
context.drawImage(player, 0, 0, canvas.width, canvas.height);
});
// Attach the video stream to the video element and autoplay.
navigator.mediaDevices.getUserMedia(constraints)
.then((stream) => {
player.srcObject = stream;
});
</script>
Don't forget to stop the video stream with
player.srcObject.getVideoTracks().forEach(track => track.stop());
Update 11/2020: The Google Developer link is (currently) dead. The original article with a LOT more explanations can still be found at web.archive.org.
The Picup app is a way to take pictures from an HTML5 page and upload them to your server. It requires some extra programming on the server, but apart from PhoneGap, I have not found another way.