I am dynamically serving user created audio using html5 tag.
Works across the board, including IE10, but not with IE9. Definitely have the right doc type tags, its running in IE9 Standards, with the html5 doctype.
The script always gets restarted 4x as my code re-applies the src, and tries again if the Error event is fired. It is always this.error.code 4 ( MEDIA_ERR_SRC_NOT_SUPPORTED ) even though both m4a ( using aac audio ) and mp3 are supported by IE9.
Things I've tried
-Our server was returning audio/x-m4a, changed it to audio/m4a
-Forcing it to use mp3.
Response headers look like
Response HTTP/1.0 200 OK
Date Wed, 22 May 2013 18:09:32 GMT
Access-Control-Allow-Origin *
Content-MD5 xOmafXnKWpXcIV8x8OQVKg==
Content-Disposition inline
Content-Language en-US
Connection close
Content-Type audio/m4a
Random Theory
Is it possible IE is breaking on different bit rates of audio? The user submitted code can be 3 different bitrates, along with mono and stereo.
Sample Code
$(this.mAudioPlayer).bind({
error: function(){
if( this.tried != this.retries ){
this.src = this.src;
this.tried++;
var reason = "Error";
switch( this.error.code ){
case 1:
reason = "Aborted";
break;
case 2:
reason = "Decode";
break;
case 3:
reason = "Network";
break;
case 4:
reason = "Not_Supported";
break;
}
HandleWarning("Audio was restarted from error state because of " + reason);
}else{
HandleError("Audio failed to load");
}
},
canplay: function(){
this.loaded = true;
if( this.autoPlay ){
this.play();
}
}
});
Fiddler includes all the class i wrote, with a ton of hacks for the mess that is html5 audio cross browser support but it errors long before any of the hacks get called, so i dont think they are causing it.
http://jsfiddle.net/Rwv7A/
Related
The script below works fine in FireFox and Chrome, but in Internet Explorer 11, it always fails (with POSITION_UNAVAILABLE).
I have set the browser to allow requests for position, and I agree to the prompt the browser presents me when requesting permission.
I'm almost certain that this worked fine a few months ago when I was last experimenting with it. What could I be missing as far as IE's settings?
<script type="text/javascript">
$(document).ready(function () {
if (Modernizr.geolocation)
{
navigator.geolocation.getCurrentPosition(positionSuccess, positionError, { enableHighAccuracy: true, maximumAge: 60000, timeout: 10000 })
}
else
{
$("#GeoError").html("Unable to retrieve current position.")
}
});
function positionSuccess(position)
{
$("#Latitude").val(position.coords.latitude);
$("#Longitude").val(position.coords.longitude);
}
function positionError(error)
{
var message = "";
// Check for known errors
switch (error.code) {
case error.PERMISSION_DENIED:
message = "This website does not have your permission to use the Geolocation API";
break;
case error.POSITION_UNAVAILABLE:
message = "Your current position could not be determined.";
break;
case error.PERMISSION_DENIED_TIMEOUT:
message = "Your current position could not be determined within the specified timeout period.";
break;
}
// If it's an unknown error, build a message that includes
// information that helps identify the situation, so that
// the error handler can be updated.
if (message == "") {
var strErrorCode = error.code.toString();
message = "Your position could not be determined due to " +
"an unknown error (Code: " + strErrorCode + ").";
}
$("#GeoError").html(message)
}
</script>
Also, I get the same failure in IE11 when I try http://html5demos.com/geo, where both FireFox and Chrome work fine.
Does you enable the Location Service?
I have had the same issue, it worked after I enabled the Location Service in my windows 2016.
This page shows how to enable the Location Service in windows 10.
I have had the same issue in IE11 only.
I had to set enableHighAccuracy to false to get it to work. Once I did that IE worked as expected.
In Internet Options, click on the Privacy tab. Uncheck the Never allow websites to request your physical location box, hit on OK.
After making these changes, the http://html5demos.com/geo now worked for me. Initially it didn't.
aha, just discovered something.
Chrome apparently uses the WIFI access points to help finding the location.
IE11 (and edge) just falls back to the default location in your windows settings, if there is no immediate GPS signal.
As discussed in a previous question, I have built a prototype (using MVC Web API, NAudio and NAudio.Lame) that is streaming live low quality audio after converting it to mp3. The source stream is PCM: 8K, 16-bit, mono and I'm making use of html5's audio tag.
On both Chrome and IE11 there is a 15-34 second delay (high-latency) before audio is heard from the browser which, I'm told, is unacceptable for our end users. Ideally the latency would be no more than 5 seconds. The delay occurs even when using the preload="none" attribute within my audio tag.
Looking more closely at the issue, it appears as though both browsers will not start playing audio until they have received ~32K of audio data. With that in mind, I can affect the delay by changing Lame's MP3 'bitrate' setting. However, if I reduce the delay (by sending more data to the browser for the same length of audio), I will introduce audio drop-outs later.
Examples:
If I use Lame's V0 encoding the delay is nearly 34 seconds which requires almost 0.5 MB of source audio.
If I use Lame's ABR_32 encoding, I can reduce the delay to 10-15 seconds but I will experience pauses and drop-outs throughout the listening session.
Questions:
Any ideas how I can minimize the start-up delay (latency)?
Should I continue investigating various Lame 'presets' in hopes of picking the "right" one?
Could it be that MP3 is not the best format for live streaming?
Would switching to Ogg/Vorbis (or Ogg/OPUS) help?
Do we need to abandon HTML5's audio tag and use Flash or a java applet?
Thanks.
You can not reduce the delay, since you have no control on the browser code and buffering size. HTML5 specification does not enforce any constraint, so I don't see any reason why it would improve.
You can however implement a solution with webaudio API (it's quite simple), where you handle streaming yourself.
If you can split your MP3's chunk in fixed size (so that each MP3 chunks size is known beforehand, or at least, at receive time), then you can have a live streaming in 20 lines of code. The chunk size will be your latency.
The key is to use AudioContext::decodeAudioData.
// Fix up prefixing
window.AudioContext = window.AudioContext || window.webkitAudioContext;
var context = new AudioContext();
var offset = 0;
var byteOffset = 0;
var minDecodeSize = 16384; // This is your chunk size
var request = new XMLHttpRequest();
request.onprogress = function(evt)
{
if (request.response)
{
var size = request.response.length - byteOffset;
if (size < minDecodeSize) return;
// In Chrome, XHR stream mode gives text, not ArrayBuffer.
// If in Firefox, you can get an ArrayBuffer as is
var buf;
if (request.response instanceof ArrayBuffer)
buf = request.response;
else
{
ab = new ArrayBuffer(size);
buf = new Uint8Array(ab);
for (var i = 0; i < size; i++)
buf[i] = request.response.charCodeAt(i + byteOffset) & 0xff;
}
byteOffset = request.response.length;
context.decodeAudioData(ab, function(buffer) {
playSound(buffer);
}, onError);
}
};
request.open('GET', url, true);
request.responseType = expectedType; // 'stream' in chrome, 'moz-chunked-arraybuffer' in firefox, 'ms-stream' in IE
request.overrideMimeType('text/plain; charset=x-user-defined');
request.send(null);
function playSound(buffer) {
var source = context.createBufferSource(); // creates a sound source
source.buffer = buffer; // tell the source which sound to play
source.connect(context.destination); // connect the source to the context's destination (the speakers)
source.start(offset); // play the source now
// note: on older systems, may have to use deprecated noteOn(time);
offset += buffer.duration;
}
I am trying to record from the microphone using HTML5 in Chrome 29 Beta for Android (it has enabled web audio support in its beta 29). In the code below, ProcessAudio is the web audio filter function, in which I receive the input buffer from the microphone. I get the correct sample size. However, the audio pcm samples in mobile chrome are always zero. Does chrome disable the input to the audio filters in its mobile version? Has anybody got audio recording working using HTML5 in chrome mobile version?
The following code works fine in chrome (desktop version).
<!DOCTYPE HTML>
<html>
<head>
<script>
function GetMedia(obj, fnSuccess, fnFailure)
{
if(navigator.getUserMedia)
{
return navigator.getUserMedia(obj, fnSuccess, fnFailure);
}
else if(navigator.webkitGetUserMedia)
{
return navigator.webkitGetUserMedia(obj, fnSuccess, fnFailure);
}
else if(navigator.mozGetUserMedia)
{
return navigator.mozGetUserMedia(obj, fnSuccess, fnFailure);
}
else if(navigator.msGetUserMedia)
{
return navigator.msGetUserMedia(obj, fnSuccess, fnFailure);
}
alert("no audio capture");
}
var incrementer = 0;
function ProcessAudio(e)
{
var inputBuffer = e.inputBuffer.getChannelData(0);
var outputBuffer = e.outputBuffer.getChannelData(0);
outputBuffer.set(inputBuffer, 0);
document.getElementById("display").innerText =
incrementer + " " + inputBuffer[0];
incrementer++;
}
var context = null;
function Success(localMediaStream)
{
context = new window.webkitAudioContext();
var microphone = context.createMediaStreamSource(localMediaStream);
var node = context.createScriptProcessor(4096, 1, 1);
node.onaudioprocess = ProcessAudio;
microphone.connect(node);
node.connect(context.destination);
}
function Error(err)
{
alert("no audio support");
}
function load(e)
{
GetMedia({audio:true,video:false}, Success, Error);
}
</script>
</head>
<body onload="load(event)">
<div>Audio Player</div>
<div id="display"></div>
<video id="localvideo" autoplay="autoplay" style="opacity:1"></video>
</body>
</html>
We now have Web Audio input working in Chrome for Android Beta (31.0.1650.11).
There's an issue with inputBuffer in Chrome beta for Android right now - it always contains zeros (at least on those devices that I tested) as you mentioned - confirmed. That ain't much of help but for demo purposes the best I could achieve right now is playing "live" input stream via <audio> object. See here. Hit "record" and then start playing the audio object. The rest beyond that is TARFU in the current beta. Guess I'm waiting for G-folks to patch it asap. What I found out though, is that zingaya.com works fine (that is, records your audio and plays it back to you) on Chrome Beta for Android. Just try them out and they'll play back your stream. Apparently, this is made by leveraging WebRTC. In other words you evidently can record an audio stream that way, I haven't figured out how to do it just yet. Post it up if you come up with something. G-guys are working fast on the other hand - they've released a new beta version less than a week ago that at least can play audio fetched with XHR. The version prior to that couldn't do that even.
I've been fiddling with the hell that is HTML5 video/audio for a couple of weeks now. Usually the reason why something failed popped up after a while, but I've been, unable to find why I get forwarding and rewinding problems in chrome.
Anyhow...
The video or audio tag is being loaded in an extjs panel when a video or audio file is requested. The files are sent as streams and they work fine in IE and firefox (after adding duration to the response header)
There's an issue with safari, but it's apparently the fact that the whole site runs in HTTPS (that's being worked on right now).
In chrome (which is my issue and is at the latest version) the video and audio loads just fine, but I'm unable to rewind or forward. When trying to seek videos just go ahead a few seconds until it reaches the end of the stream. the audio also plays just fine but trying to rewind (or forward) multiple times simply breaks the progress bar and stops the audio from playing on.
I'm not entirely sure what's being sent from the server, but I'm wondering if this might be caused by missing data in the response. If it's not that anything else to point me towards a fix is just as welcome. I think I've covered pretty much the whole set up and I've made sure that there's a source tag for each browser.
edit: this is the code generated by the javascript for one of the files:
<video width="1889" height="2" preload="auto" autoplay="1" controls="1" id="videoPlayer" style="width: 1889px; height: 233px; ">
<source src="http://localhost:8080/epaServer/epa/documents/496.ds_webm?sessionId=5616fde4-50af-43d6-a57c-f06540b64fcb" type="video/webm">
<source src="http://localhost:8080/epaServer/epa/documents/496.ds_mp4?sessionId=5616fde4-50af-43d6-a57c-f06540b64fcb" type="video/mp4">
<div>Your browser doesn't support html5 video. <a>Upgrade Chrome</a></div>
</video>
I've also found that I can't seek any of the files even if I open them separately from the application.
I've tried to find more info on my own these are the headers chrome shows in the network tab:
Request URL:https://localhost:8443/epaServer/epa/documents/496.ds_webm?sessionId=5616fde4-50af-43d6-a57c-f06540b64fcb
Request Method:GET
Status Code:200 OK
Request Headers
Accept:/
Accept-Charset:ISO-8859-1,utf-8;q=0.7,*;q=0.3
Accept-Encoding:identity;q=1, *;q=0
Accept-Language:en-US,en;q=0.8
Connection:keep-alive
Cookie:sessionId=5616fde4-50af-43d6-a57c-f06540b64fcb
Host:localhost:8443
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.168 Safari/535.19
Query String Parametersview URL encoded
sessionId:5616fde4-50af-43d6-a57c-f06540b64fcb
Response Headers
Cache-Control:private
Content-Length:1588816
Content-Type:video/webm
Date:Mon, 14 May 2012 14:23:02 GMT
Expires:Thu, 01 Jan 1970 01:00:00 CET
Server:Apache-Coyote/1.1
X-Content-Duration:17.31
>
I found the reason why it's not working on this question:
HTML5 video will not loop
Our server doesn't understand partial content right now.
As a result chrome is sending requests for content that doesn't get answered which in turn makes our video's and audio unseekable (and unloopable).
You must handle req.headers['range'] which Chrome will send to your streaming server.
Please refer to my codes below. It worked well on Chrome, Firefox, Edge and IE. I haven't test it on Safari but hopefully it also can work.
I used Sails/Nodejs backend and gridFS/mongodb database for storing Videos files as Chunks.
try {
let foundMetaFile = await GridFS.findOne({id: fileId});
if (!foundMetaFile) return res.status(400).json(Res.error(undefined, {message: `invalid ${fileId} file`}));
let fileLength = foundMetaFile['length'];
let contentType = foundMetaFile['contentType'];
// let chunkSize = foundMetaFile['chunkSize'];
if(req.headers['range']) {
// Range request, partialle stream the file
console.log('Range Reuqest');
var parts = req.headers['range'].replace(/bytes=/, "").split("-");
var partialStart = parts[0];
var partialEnd = parts[1];
var start = parseInt(partialStart, 10);
var end = partialEnd ? parseInt(partialEnd, 10) : fileLength - 1;
var chunkSize = (end - start) + 1;
console.log('Range ', start, '-', end);
res.writeHead(206, {
'Content-Range': 'bytes ' + start + '-' + end + '/' + fileLength,
'Accept-Ranges': 'bytes',
'Content-Length': chunkSize,
'Content-Type': contentType
});
}
let { mongodbConnection } = global;
let bucket = new GridFSBucket(mongodbConnection, { bucketName: 'fs' });
return new Promise ((resolve, reject) => {
let downloadStream = bucket.openDownloadStream(fileId);
downloadStream.on('error', (err) => {
console.log("Received Error stream")
res.end();
reject(err);
})
downloadStream.on('end', () => {
console.log("Received End stream");
res.end();
resolve(true);
})
console.log("start streaming");
downloadStream.pipe(res);
})
} catch (error) {
switch (error.name) {
case 'UsageError':
return res.status(400).json(Res.error(undefined, {message: 'invalid Input'}));
case 'AdapterError':
return res.status(400).json(Res.error(undefined, {message: 'adapter Error'}));
default:
return res.serverError(error);
}
I have a video as a background to a web page, and I am trying to get it to loop. Here is the code:
<video autoplay='true' loop='true' muted='true'>
<source src='/admin/wallpapers/linked/4ebc66e899727777b400003c' type='video/mp4'></source>
</video>
Even though I have told the video to loop, it does not. I also tried to get it to loop with the onended attribute (as per this Mozilla support thread, I also tried that bit of jQuery). Nothing has worked so far. Is it an issue with Chrome, or my code?
Edit:
I checked the Network events and HEAD of a working copy (http://fhsclock-labs.heroku.com/no-violence) versus the application I'm trying to get working. The difference is the working copy is serving up the video from a static asset on Heroku (via Varnish, apparently), whilst mine is serving from GridFS (MongoDB).
The Network tab of Chrome's Inspector show that in my application, the video is requested three times. One time the Status is "pending", the second is "canceled", and the final one is 200 OK. The working copy only shows two requests, one's Status is pending and the other is 206 Partial Content. However, after the video plays once, that request changes to "Cancelled" and it makes another request for that video. In my application, that does not happen.
As for Type, in my application, two are "undefined" and the other "video/mp4" (which it is supposed to be). In the working app, all of the requests are "video/mp4".
In addition, I'm getting Resource interpreted as Other but transferred with MIME type undefined. warnings in the Console.
I'm not really quite sure where to begin on this. It's my belief that the issue is server-side, as serving the file as static assets works fine. It could be that the server isn't sending the correct content type. It could be an issue with GridFS. I do not know.
At any rate, the source is here. Any insight that you can offer is appreciated.
Ah, I just stumbled into this exact problem.
As it turns out, looping (or any sort of seeking, for that matter) in <video> elements on Chrome only works if the video file was served up by a server that understands partial content requests. i.e. the server needs to honor requests that contain a "Range" header with a 206 "Partial Content" response. This is even the case if the video is small enough to be fully buffered by chrome, and no more server-round trips are made: if your server didn't honor chrome's Range request the first time, the video will not be loopable or seekable.
So yes, an issue with GridFS, although arguably Chrome should be more forgiving.
Simplest workaround:
$('video').on('ended', function () {
this.load();
this.play();
});
The 'ended' event fires when the video reaches the end, video.load() resets the video to the beginning, and video.play() starts it playing immediately once loaded.
This works well with Amazon S3 where you don't have as much control over server responses, and also gets around Firefox issues related to video.currentTime not being settable if a video is missing its length metadata.
Similar javascript without jQuery:
document.getElementsByTagName('video')[0].onended = function () {
this.load();
this.play();
};
Looks like its been an issue in the past, there are at least two closed bugs on it, but both state that it was fixed:
http://code.google.com/p/chromium/issues/detail?id=39683
http://code.google.com/p/chromium/issues/detail?id=18846
Since Chrome and Safari both use webkit based browsers you might be able to use some of these work arounds:
http://blog.millermedeiros.com/2011/03/html5-video-issues-on-the-ipad-and-how-to-solve-them/
function restartVideo(){
vid.currentTime = 0.1; //setting to zero breaks iOS 3.2, the value won't update, values smaller than 0.1 was causing bug as well.
vid.play();
}
//loop video
vid.addEventListener('ended', restartVideo, false);
Just in case none of the answers above help you, make sure you don't have your inspector running with the Disable cache option checked. Since Chrome grabs the video from cache, it will basically work once. Just debugged this for 20 minutes before realizing this was the cause. For reference and so I know I am not the only one someone else's chromium bug report.
My situation:
I have the exact same problem, however changing the header of the response message alone didnt do. No loop, replay or seek. Also a pure stop doesnt work, but that could be my configuration.
Answer:
According to some sites (couldnt find them anymore) its also possible to trigger the load() method right after the video ends, and before the next one is supposed to start. That should reload the source causing a once again working video/audio element.
#john
Please note that your answers/links are normal bugs, and not focused on this problem. Using a server/webserver is what causes this problem. Whereas the bugs these links describe are of a different kind. Thats also why the answer isnt working.
I hope it helps, i am still looking for a solution.
For anyone coming on this page 9 years later and if all the above answers didn't work: I had this issue too and I thought the source of the issue was either my browsers or with the server.
I've later noticed that the other websites on internet which use looping videos they don't have issue with looping videos. To troubleshoot I have downloaded a random video from one of the sites and I visited and uploaded on my own server to delightedly find out it was working, so it seemed that the source of the issue was the video I was using.
Then I fixed my video with an online video converter website (don't want to publicize any in particular but the first ones from a quick google research do work) and alas, this solved the issue.
I'm not sure what the real reason of the issue was. I do assume there was a conversion or compression error of the original video that was handed me from my client.
I know this doesn't pertain exactly to the question asked, but if someone comes across this when having a similar issue, make sure that you have your sources in order properly.
I was loading an mp4 and a webm file and noticed that the video was not looping in Chrome. It was because the webm file was the first source listed so Chrome was loading the webm file and not the mp4.
Hope that helps someone else that comes across this issue.
<video autoplay loop>
<source src="/path-to-vid/video.mp4" type="video/mp4">
<source src="/path-to-vid/video.webm" type="video/webm">
</video>
it is super lame but dropbox use the right status code. So upload to dropbox and replace the www by dl.
Thus using a dropbox url the video play fine.
I had same issue and inevitably solved problem by streaming the content.
e.g this is the code with PHP laravel blade html code which is requesting to streaming route:
<video>
<source src="{{route('getVideoStream',$videoId)}}" type="video/mp4"/>
</video>
in the Controller I will stream video and return it as laravel stream function:
public function getVideoStream($videoId){
$path = $pathOfVideo;
$headers = [
'Content-Type' => 'video/mp2t',
'Content-Length' => File::size($path),
'Content-Disposition' => 'attachment; filename="start.mp4"'
];
$stream = new VideoStream($path);
return response()->stream(function () use ($stream) {
$stream->start();
});
}
and VideoStream Class is the streaming class I found from a GitHub gist:
class VideoStream
{
private $path = "";
private $stream = "";
private $buffer = 102400;
private $start = -1;
private $end = -1;
private $size = 0;
function __construct($filePath)
{
$this->path = $filePath;
}
/**
* Open stream
*/
private function open()
{
if (!($this->stream = fopen($this->path, 'rb'))) {
die('Could not open stream for reading');
}
}
/**
* Set proper header to serve the video content
*/
private function setHeader()
{
ob_get_clean();
header("Content-Type: video/mp4");
header("Cache-Control: max-age=2592000, public");
header("Expires: " . gmdate('D, d M Y H:i:s', time() + 2592000) . ' GMT');
header("Last-Modified: " . gmdate('D, d M Y H:i:s', #filemtime($this->path)) . ' GMT');
$this->start = 0;
$this->size = filesize($this->path);
$this->end = $this->size - 1;
header("Accept-Ranges: 0-" . $this->end);
if (isset($_SERVER['HTTP_RANGE'])) {
$c_start = $this->start;
$c_end = $this->end;
list(, $range) = explode('=', $_SERVER['HTTP_RANGE'], 2);
if (strpos($range, ',') !== false) {
header('HTTP/1.1 416 Requested Range Not Satisfiable');
header("Content-Range: bytes $this->start-$this->end/$this->size");
exit;
}
if ($range == '-') {
$c_start = $this->size - substr($range, 1);
} else {
$range = explode('-', $range);
$c_start = $range[0];
$c_end = (isset($range[1]) && is_numeric($range[1])) ? $range[1] : $c_end;
}
$c_end = ($c_end > $this->end) ? $this->end : $c_end;
if ($c_start > $c_end || $c_start > $this->size - 1 || $c_end >= $this->size) {
header('HTTP/1.1 416 Requested Range Not Satisfiable');
header("Content-Range: bytes $this->start-$this->end/$this->size");
exit;
}
$this->start = $c_start;
$this->end = $c_end;
$length = $this->end - $this->start + 1;
fseek($this->stream, $this->start);
header('HTTP/1.1 206 Partial Content');
header("Content-Length: " . $length);
header("Content-Range: bytes $this->start-$this->end/" . $this->size);
} else {
header("Content-Length: " . $this->size);
}
}
/**
* close curretly opened stream
*/
private function end()
{
fclose($this->stream);
exit;
}
/**
* perform the streaming of calculated range
*/
private function stream()
{
$i = $this->start;
set_time_limit(0);
while (!feof($this->stream) && $i <= $this->end) {
$bytesToRead = $this->buffer;
if (($i + $bytesToRead) > $this->end) {
$bytesToRead = $this->end - $i + 1;
}
$data = fread($this->stream, $bytesToRead);
echo $data;
flush();
$i += $bytesToRead;
}
}
/**
* Start streaming video content
*/
function start()
{
$this->open();
$this->setHeader();
$this->stream();
$this->end();
}
}