HTML5 video will not loop - google-chrome

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();
}
}

Related

Autoplay stops after some time html5 video taken from google drive

I have a mp4 video inside html video tag. SRC= is given for the google drive video.
see my code below
<video auto play muted loop id="video" class="" src="https://drive.google.com/uc?export=download&id=1qSC6ySf6ZZldFRpuBx9EXvDsD-mfve1Z" type="video/mp4"> </video>
Note: when I am directly calling mp4 video from my server than it will not pause but when the video is called from google drive it gets paused after few minutes. As I am not showing any controls. I need the video to be played continuously.
the issues I'm getting is
Codepen link: https://codepen.io/5salman/pen/bGRMyKj
There's nothing inherit in Google Drive (that I can think of) that would cancel autoplay. More likely you're hitting an error event. The video will be loaded via "206 Partial Content" byte-range requests. If it doesn't end up being locally cached then that could make it more susceptible to network issues than on a server that doesn't support byte-range requests. Also consider looking at the network Devtools for any network errors.
Mitigation options:
Reduce the size of your video! It's 14mb for 20 seconds of video. Use Handbrake or something to re-encode the video to a smaller bitrate/size. That will reduce network traffic and make it less likely to choke the viewing device.
Add an error event handler on the <video> tag - this can help you with diagnostics, and you can also trigger the video to (try) to play again.
function reloadVideo(vidElement, preserveTime = true) {
if (preserveTime) {
// wait to set the current time again until after playable
const position = vidElement.currentTime;
var cb = vidElement.addEventListener('canplay', () => {
if (!isNaN(position) && position < vidElement.duration) {
// I recommend seeking just a frame or so ahead, just in case there was a decode error on the video
vidElement.currentTime = position + 1/30;
}
vidElement.removeEventListener('canplay', cb);
});
}
const src = vidElement.currentSrc;
vidElement.src = "";
vidElement.src = src;
}
var vidElement = document.querySelector('#video');
// retry on error
var retryErrorCodes = [MediaError.MEDIA_ERR_NETWORK, MediaError.MEDIA_ERR_DECODE];
var SECONDS_BEFORE_RETRY = 5;
vidElement.addEventListener('error', evt => {
if (retryErrorCodes.includes(vidElement.error.code)) {
setTimeout(reloadVideo, SECONDS_BEFORE_RETRY * 1000, vidElement);
}
}
(Not Recommended) if you need it to keep running you can add a setInterval function that restarts the video if it's paused. Keep in mind that these "zombie" functions can get annoying if you don't manage them carefully.
// use setInterval to keep retrying the playback
var SECONDS_BETWEEN_PLAYING_CHECKS = 10;
var keepPlayingTimer = setInterval(function () {
var vidElement = document.querySelector('#video');
// make sure the element is still there
if (vidElement && typeof vidElement.play === 'function') {
// if it's not playing start it playing again
if (vidElement.paused) {
vidElement.play();
}
} else {
// don't call this function again if video element is gone
clearInterval(keepPlayingTimer);
}
}, SECONDS_BETWEEN_PLAYING_CHECKS * 1000);

How to edit Firefox's builtin HTML5 video player controls

Currently there is a global file loaded in Firefox chrome://global/content/bindings/videocontrols.xml, which has values for the things like seek time and playback rate for native HTML5 videos.
File can be found here: https://github.com/mozilla/gecko-dev/blob/master/toolkit/content/widgets/videocontrols.xml
https://support.mozilla.org/en-US/questions/1063314#answer-732595 Has a great post on how to edit this file and save it, but the link they provided (guide) is different from the original posting, so it's not easy to implement.
The file does contain:
case "accel-rightArrow": /* Seek forward 10% */
oldval = this.video.currentTime;
var maxtime = (this.video.duration || this.maxCurrentTimeSeen / 1000);
if (keystroke == "rightArrow") {
newval = oldval + 15;
} else {
newval = oldval + maxtime / 10;
}
this.video.currentTime = (newval <= maxtime ? newval : maxtime);
Which can edited, but since it's somewhere in /lib/ in a jar file, doesn't seem easily editable.
Is there a simple way, even in about:config to edit the native HTML5 videoplayer keybindings in Firefox?
Javascript Method:
https://developer.mozilla.org/de/docs/Web/HTML/Using_HTML5_audio_and_video
This link provides information on how to control video player, so a possible user script is another option if changing videocontrols.xml is not viable.

Using html5 to capture microphone input on mobile chrome

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.

HTML5 Audio ERR_SRC_NOT_SUPPORTED in IE9

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/

How to browse mobile directory in flex?

I have captured 3 videos on my mobile which is by default stored on the phone gallery (Gallery/videos/). I have to play these 3 videos in one of my flex mobile application. How can I get the videos to the flex project? if I need to browse the mobile directory means kindly help me with some code to do so.
I too am looking for an answer to this question. Right now, based on other Stackoverflow discussions, exhaustive perusal of tutorials and Adobe documentation, and comments to both (often the more useful resource), I'm coming to the conclusion that it's not possible.
you can use CameraRoll.browseForImage() and open the iOS gallery of photos to see all entities of MediaType.IMAGE, but it will not show you MediaType.VIDEO
you can use CameraUI to launch the system camera by delegation and that returns a MediaPromise, but as far as I can tell, it does not save the video you capture anywhere, and I cannot find a way to access the captured video using the MediaPromise (at least using the Loader class)
Here's my code as a hint in that direction. The second code block is using the CameraRoll to browseForImage() but there is no browseForVideo() in the API.
if(CameraUI.isSupported)
{
camera = new CameraUI();
camera.addEventListener(MediaEvent.COMPLETE, videoMediaEventComplete);
camera.addEventListener(Event.CANCEL, cameraCanceled);
camera.addEventListener(ErrorEvent.ERROR, cameraError);
camera.launch(MediaType.VIDEO);
}
else
{
statusText.text = "Camera not supported on this device.";
startTimer();
}
if (CameraRoll.supportsBrowseForImage)
{
roll = new CameraRoll();
roll.addEventListener(MediaEvent.SELECT, cameraRollEventComplete);
roll.addEventListener(Event.CANCEL, cameraCanceled);
roll.addEventListener(ErrorEvent.ERROR, cameraError);
roll.browseForImage();
}
else
{
statusText.text = "Camera roll not supported on this device.";
startTimer();
}
I've since found that Videos captured using the delegated system camera are stored in a temporary storage location that iOS -DOES!- allow access to. (I was pleasantly shocked.)
The Captured video is not added to the device's Camera Roll as other videos captured using the iOS System Camera app, so it's not enough to capture video and expect to be able to access it later (if, for instance, CameraRoll.browseForVideo() is ever added to the API.
Therefore, you have to 'get while the getting is good' and move the file from the temporary storage location to some non-volatile location such as ApplicationStorageDirectory or the user's Documents directory (The only options in iOS I think).
The MediaPromise... I think... is completely useless for accessing the video via any direct progressive loader/streamer method, but still provides the location/url/path/filename of the temporary file so you can perform File operations on it.
Ironic that there are tutorials for getting around the lack of a file location/url/path/filename in the MediaPromise when using CameraRoll.browseForImage()... and that method is to use a loader class to load the image content (which you can then write out to a file), but when taking video, the video content is not accessible, and instead a file location/url/path/filename is provided. Ironic that there are nearly no resources I was able to find to help with this also. grumble
I'm going to include some code chunks w/o really editing them to strip out extraneous bits because it's way past when I need to be in bed, but I wanted you to have this. I may come clean it up later.
This section is in a Spark SkinnablePopUpContainer and I use the same click event for several buttons, thus the below 'case' is in the switch-case in that event handler function.
In case you are not familiar, the 'close(true, data)' is the method to close the SkinnablePopUpContainer, tell the parent/owner that the container was closed purposefully and that it should look for the data object being shared back (i.e., there are changes to be 'commit'ed).
case "cameraVideo":
{
if(CameraUI.isSupported)
{
camera = new CameraUI();
camera.addEventListener(MediaEvent.COMPLETE, videoMediaEventComplete);
camera.addEventListener(Event.CANCEL, cameraCanceled);
camera.addEventListener(ErrorEvent.ERROR, cameraError);
camera.launch(MediaType.VIDEO);
}
else
{
statusText.text = "Camera not supported on this device.";
startTimer();
}
break;
}
protected function cameraCanceled(event:Event):void
{
statusText.text = "Camera access canceled by user.";
startTimer();
}
protected function cameraError(event:ErrorEvent):void
{
statusText.text = "There was an error while trying to use the camera.";
startTimer();
}
protected function videoMediaEventComplete(event:MediaEvent):void
{
statusText.text="Preparing captured video...";
camera.removeEventListener(MediaEvent.COMPLETE, videoMediaEventComplete);
camera.removeEventListener(Event.CANCEL, cameraCanceled);
camera.removeEventListener(ErrorEvent.ERROR, cameraError);
var media:MediaPromise = event.data;
data.MediaType = MediaType.VIDEO;
data.MediaPromise = media;
data.source = "camera video";
close(true,data)
}
This section is the Actionscript in the close handler of the parent/owner of the SkinnablePopUpContainer (truncated once the useful code is included)
private function choosePictureLightboxClosed(event:PopUpEvent):void
{
imageButtonsActive = false;
if(event.commit)
{
this.data = event.data as Object;
filters = new Array();
selection = true;
switch(data.MediaType)
{
case MediaType.VIDEO:
{
mediaType = "video";
trace(data.MediaPromise.file.url + " - " + data.MediaPromise.relativePath + " - " +data.MediaPromise.mediaType);
var sourceFile:File = new File(data.MediaPromise.file.url);
var destinationFile:File = File.applicationStorageDirectory.resolvePath("User" +parentApplication.userid);
if(destinationFile.exists && !destinationFile.isDirectory)
{
destinationFile.deleteFile();
}
destinationFile.createDirectory();
destinationFile = destinationFile.resolvePath("Videos");
if(destinationFile.exists && !destinationFile.isDirectory)
{
destinationFile.deleteFile();
}
destinationFile.createDirectory();
destinationFile = destinationFile.resolvePath(parentApplication.userid+"Video"+new Date().getTime()+".mov");
trace(destinationFile.nativePath);
sourceFile.moveTo(destinationFile,true);
break;
}
I sure do hope this helps. This has been a very frustrating (and costly in terms of our project being government grant funded and having deadlines we utterly failed to meet), and I very much hope that these hard-won solutions might help others avoid the same experience.