i'm trying to build a video player, that works everywhere. so far i'd be going with:

<video>
    <source src="video.mp4"></source>
    <source src="video.ogv"></source>
    <object data="flowplayer.swf" type="application/x-shockwave-flash">
        <param name="movie" value="flowplayer.swf" />
        <param name="flashvars" value='config={"clip":"video.mp4"}' />
    </object>
</video>

(as seen on several sites, for example video for everybody) so far, so good.

but now i also want some kind of playlist/menu along with the video player, from which i can select other videos. those should be opened within my player right away. so i will have to "dynamically change the source of the video" (as seen on dev.opera.com/articles/everything-you-need-to-know-html5-video-audio/ - section "Let's look at another movie") with javascript. let's forget about the flashplayer (and thus IE) part for the time being, i will try to deal with that later.

so my JS to change the <source> tags should be something like:

<script>
function loadAnotherVideo() {
    var video = document.getElementsByTagName('video')[0];
    var sources = video.getElementsByTagName('source');
    sources[0].src = 'video2.mp4';
    sources[1].src = 'video2.ogv';
    video.load();
}
</script>

problem is, this doesnt work in all browsers. namely, firefox =O there is a nice page, where you can observe the problem i'm having: http://www.w3.org/2010/05/video/mediaevents.html

as soon as i trigger the load() method (in firefox, mind you), the video player dies.

now i have found out that when i don't use multiple <source> tags, but instead just one src attribute within the <video> tag, the whole thing DOES work in firefox.

so my plan is to just use that src attribute and determine the appropriate file using the canPlayType() function.

am i doing it wrong somehow or complicating things??

upvote
  flag
That sounds just fine to me. How is it "complicating" to simplify the markup? – Matt Ball
upvote
  flag
the problem is i see myself running into a lot of javascript and distinction of cases. if maybe i missed something, like there was a way to make it work in firefox WITH multiple <source> tags. then i suppose that would be easier – sashn
upvote
  flag
did you ever figure out about the flash part on ie8? – Neeraj
upvote
  flag
@Neeraj the final solution involved the plugin video.js which uses a flash player as a fallback for IE8 and the likes. there might be superior plugins today though. using the plugin also did not help with the firefox issue regarding the load() method, which was the initial motivation for this post. today, that issue has long been fixed, it seems. – sashn

14 Answers 11

Try moving the OGG source to the top. I've noticed Firefox sometimes gets confused and stops the player when the one it wants to play, OGG, isn't first.

Worth a try.

upvote
  flag
didn't work, unfortunately. worth a try though, yeah. – sashn

Modernizr worked like a charm for me.

What I did is that I didn't use <source>. Somehow this screwed things up, since the video only worked the first time load() was called. Instead I used the source attribute inside the video tag -> <video src="blabla.webm" /> and used Modernizr to determine what format the browser supported.

<script>
var v = new Array();

v[0] = [
        "videos/video1.webmvp8.webm",
        "videos/video1.theora.ogv",
        "videos/video1.mp4video.mp4"
        ];
v[1] = [
        "videos/video2.webmvp8.webm",
        "videos/video2.theora.ogv",
        "videos/video2.mp4video.mp4"
        ];
v[2] = [
        "videos/video3.webmvp8.webm",
        "videos/video3.theora.ogv",
        "videos/video3.mp4video.mp4"
        ];

function changeVid(n){
    var video = document.getElementById('video');

    if(Modernizr.video && Modernizr.video.webm) {
        video.setAttribute("src", v[n][0]);
    } else if(Modernizr.video && Modernizr.video.ogg) {
        video.setAttribute("src", v[n][1]);
    } else if(Modernizr.video && Modernizr.video.h264) {
        video.setAttribute("src", v[n][2]);
    }

    video.load();
}
</script>

Hopefully this will help you :)

If you don't want to use Modernizr , you can always use CanPlayType().

upvote
  flag
Don't you mean v[n][1] for the second if (Modernizr.video && Modernizr.video.ogg) is true? – bergie3000
upvote
  flag
@bergie3000 I most certainly do. Thanks for pointing it out. – Marius Engen Haugen
1 upvote
  flag
After trying other methods, I tried this Modernizr approach and it worked well. Chrome (for some reason) wouldn't load an mp4 in my case, but would load a webm. Thanks @MariusEngenHaugen – Adam Hey
upvote
  flag
Glad I could be of service @AdamHey – Marius Engen Haugen

Instead of getting the same video player to load new files, why not erase the entire <video> element and recreate it. Most browsers will automatically load it if the src's are correct.

Example (using Prototype):

var vid = new Element('video', { 'autoplay': 'autoplay', 'controls': 'controls' });
var src = new Element('source', { 'src': 'video.ogg', 'type': 'video/ogg' });

vid.update(src);
src.insert({ before: new Element('source', { 'src': 'video.mp4', 'type': 'video/mp4' }) });

$('container_div').update(vid);
5 upvote
  flag
That significantly slows down the overall browser since even after the video tag is removed, the browser still loads the removed videos till the end. – Moe Sweet

I have a similar web app and am not facing that sort of problem at all. What i do is something like this:

var sources = new Array();

sources[0] = /path/to/file.mp4
sources[1] = /path/to/another/file.ogg
etc.....

then when i want to change the sources i have a function that does something like this:

this.loadTrack = function(track){
var mediaSource = document.getElementsByTagName('source')[0];
mediaSource.src = sources[track];

    var player = document.getElementByTagName('video')[0];
    player.load();

}

I do this so that the user can make their way through a playlist, but you could check for userAgent and then load the appropriate file that way. I tried using multiple source tags like everyone on the internet suggested, but i found it much cleaner, and much more reliable to manipulate the src attribute of a single source tag. The code above was written from memory, so i may have glossed over some of hte details, but the general idea is to dynamically change the src attribute of the source tag using javascript, when appropriate.

Using the <source /> tags proved difficult for me in Chrome 14.0.835.202 specifically, although it worked fine for me in FireFox. (This could be my lack of knowledge, but I thought an alternate solution might be useful anyway.) So, I ended up just using a <video /> tag and setting the src attribute right on the video tag itself. The canPlayVideo('<mime type>') function was used to determine whether or not the specific browser could play the input video. The following works in FireFox and Chrome.

Incidently, both FireFox and Chrome are playing the "ogg" format, although Chrome recommends "webm". I put the check for browser support of "ogg" first only because other posts have mentioned that FireFox prefers the ogg source first (i.e. <source src="..." type="video/ogg"/> ). But, I haven't tested (and highly doubt) whether or not it the order in the code makes any difference at all when setting the "src" on the video tag.

HTML

<body onload="setupVideo();">
    <video id="media" controls="true" preload="auto" src="">
    </video>
</body>

JavaScript

function setupVideo() {
       // You will probably get your video name differently
       var videoName = "http://video-js.zencoder.com/oceans-clip.mp4";

       // Get all of the uri's we support
       var indexOfExtension = videoName.lastIndexOf(".");
       //window.alert("found index of extension " + indexOfExtension);
       var extension = videoName.substr(indexOfExtension, videoName.length - indexOfExtension);
       //window.alert("extension is " + extension);
       var ogguri = encodeURI(videoName.replace(extension, ".ogv"));
       var webmuri = encodeURI(videoName.replace(extension, ".webm"));
       var mp4uri = encodeURI(videoName.replace(extension, ".mp4"));
       //window.alert(" URI is " + webmuri);


       // Get the video element
       var v = document.getElementById("media");
       window.alert(" media is " + v);

       // Test for support
       if (v.canPlayType("video/ogg")) {
            v.setAttribute("src", ogguri);
           //window.alert("can play ogg");
       }
       else if (v.canPlayType("video/webm")) {
           v.setAttribute("src", webmuri);
           //window.alert("can play webm");
       }
       else if (v.canPlayType("video/mp4")) {
           v.setAttribute("src", mp4uri);
           //window.alert("can play mp4");
       }
       else {
           window.alert("Can't play anything");
       }

      v.load();
      v.play();
  }

Your original plan sounds fine to me. You'll probably find more browser quirks dealing with dynamically managing the <source> elements, as indicated here by the W3 spec note:

Dynamically modifying a source element and its attribute when the element is already inserted in a video or audio element will have no effect. To change what is playing, just use the src attribute on the media element directly, possibly making use of the canPlayType() method to pick from amongst available resources. Generally, manipulating source elements manually after the document has been parsed is an unncessarily[sic] complicated approach.

http://dev.w3.org/html5/spec/Overview.html#the-source-element

According to the spec

Dynamically modifying a source element and its attribute when the element is already inserted in a video or audio element will have no effect. To change what is playing, just use the src attribute on the media element directly, possibly making use of the canPlayType() method to pick from amongst available resources. Generally, manipulating source elements manually after the document has been parsed is an unncessarily complicated approach.

So what you are trying to do is apparently not supposed to work.

upvote
  flag
Don't read existing answers? – greg.kindel
upvote
  flag
@greg.kindel I noticed after I posted... but the reason I didn't notice in the first place is that your intro text is confusing. Which original plan are we talking about? The original original plan that won't work or the updated original plan that follows the guidance that we both posted? – Yaur
upvote
  flag
fair enough, I should have quoted. I meant his plan to "just use that src attribute and determine the appropriate file using the canPlayType() function" which does work. – greg.kindel

Yaur: Although what you have copied and pasted is good advice, this does not mean that it is impossible to change the source element of an HTML5 video element elegantly, even in IE9 (or IE8 for that matter).(This solution does NOT involve replacing the entire video element, as it is bad coding practice).

A complete solution to changing/switching videos in HTML5 video tags via javascript can be found here and is tested in all HTML5 browser (Firefox, Chrome, Safari, IE9, etc).

If this helps, or if you're having trouble, please let me know.

upvote
  flag
Brilliant! This is the way to do it. You don't even need jQuery; in the linked code, substitute: mp4Vid.setAttribute('src', "/pathTo/newVideo.mp4"); – Velojet

I hated all these answers because they were too short or relied on other frameworks.

Here is "one" vanilla JS way of doing this, working in Chrome, please test in other browsers:

http://jsfiddle.net/mattdlockyer/5eCEu/2/

HTML:

<video id="video" width="320" height="240"></video>

JS:

var video = document.getElementById('video');
var source = document.createElement('source');

source.setAttribute('src', 'http://www.tools4movies.com/trailers/1012/Kill%20Bill%20Vol.3.mp4');

video.appendChild(source);
video.play();

setTimeout(function() {  
    video.pause();

    source.setAttribute('src', 'http://www.tools4movies.com/trailers/1012/Despicable%20Me%202.mp4'); 

    video.load();
    video.play();
}, 3000);
16 upvote
  flag
6 upvote
  flag
This was the cleanest and most efficient for me. – Phil McCarty
upvote
  flag
Nice! It even works on Chromecast – Joaquin Iurchuk
upvote
  flag
Simply and works, thanks a lot! – Alex Baumgertner
upvote
  flag
I couldn't get Chrome to play ball with this. ONLY if I set the video src attribute to the webm path – Adam Hey
upvote
  flag
video.load doesn't work on opera(webkit version) – l3est
upvote
  flag

I have been researching this for quite a while and I am trying to do the same thing, so hopefully this will help someone else. I have been using crossbrowsertesting.com and literally testing this in almost every browser known to man. The solution I've got currently works in Opera, Chrome, Firefox 3.5+, IE8+, iPhone 3GS, iPhone 4, iPhone 4s, iPhone 5, iPhone 5s, iPad 1+, Android 2.3+, Windows Phone 8.

Dynamically Changing Sources

Dynamically changing the video is very difficult, and if you want a Flash fallback you will have to remove the video from the DOM/page and re-add it so that Flash will update because Flash will not recognize dynamic updates to Flash vars. If you're going to use JavaScript to change it dynamically, I would completely remove all <source> elements and just use canPlayType to set the src in JavaScript and break or return after the first supported video type and don't forget to dynamically update the flash var mp4. Also, some browsers won't register that you changed the source unless you call video.load(). I believe the issue with .load() you were experiencing can be fixed by first calling video.pause(). Removing and adding video elements can slow down the browser because it continues buffering the removed video, but there's a workaround.

Cross-browser Support

As far as the actual cross-browser portion, I arrived at Video For Everybody as well. I already tried the MediaelementJS Wordpress plugin, which turned out to cause a lot more issues than it resolved. I suspect the issues were due to the Wordpress plug-in and not the actually library. I'm trying to find something that works without JavaScript, if possible. So far, what I've come up with is this plain HTML:

<video width="300" height="150" controls="controls" poster="http://sandbox.thewikies.com/vfe-generator/images/big-buck-bunny_poster.jpg" class="responsive">
<source src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.ogv" type="video/ogg" />
<source src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4" type="video/mp4" />
<source src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.webm" type="video/webm" />
<source src="http://alex-watson.net/wp-content/uploads/2014/07/big_buck_bunny.iphone.mp4" type="video/mp4" />
<source src="http://alex-watson.net/wp-content/uploads/2014/07/big_buck_bunny.iphone3g.mp4" type="video/mp4" />
<object type="application/x-shockwave-flash" data="http://releases.flowplayer.org/swf/flowplayer-3.2.1.swf" width="561" height="297">
    <param name="movie" value="http://releases.flowplayer.org/swf/flowplayer-3.2.1.swf" />
    <param name="allowFullScreen" value="true" />
    <param name="wmode" value="transparent" />
    <param name="flashVars" value="config={'playlist':['http://sandbox.thewikies.com/vfe-generator/images/big-buck-bunny_poster.jpg',{'url':'http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4','autoPlay':false}]}" />
    <img alt="No Video" src="http://sandbox.thewikies.com/vfe-generator/images/big-buck-bunny_poster.jpg" width="561" height="297" title="No video playback capabilities, please download the video below" />
</object>
<strong>Download video:</strong>  <a href="video.mp4">MP4 format</a> | <a href="video.ogv">Ogg format</a> | <a href="video.webm">WebM format</a>
</video>

Important notes:

  • Ended up putting the ogg as the first <source> because Mac OS Firefox quits trying to play the video if it encounters an MP4 as the first <source>.
  • The correct MIME types are important .ogv files should be video/ogg, not video/ogv
  • If you have HD video, the best transcoder I've found for HD quality OGG files is Firefogg
  • The .iphone.mp4 file is for iPhone 4+ which will only play videos that are MPEG-4 with H.264 Baseline 3 Video and AAC audio. The best transcoder I found for that format is Handbrake, using the iPhone & iPod Touch preset will work on iPhone 4+, but to get iPhone 3GS to work you need to use the iPod preset which has much lower resolution which I added as video.iphone3g.mp4.
  • In the future we will be able to use a media attribute on the <source> elements to target mobile devices with media queries, but right now the older Apple and Android devices don't support it well enough.

Edit:

  • I'm still using Video For Everybody but now I've transitioned to using FlowPlayer, to control the Flash fallback, which has an awesome JavaScript API that can be used to control it.
upvote
  flag
This seems to be the answer to the question "How do I play video cross browser?" and not this question which is "How to I set up a playlist for my video?". – Quentin
upvote
  flag
@Quentin It was, to begin with, because that's how I found this question via Google and OP said he's trying to build a video player that works everywhere. I updated the answer to address his dynamic question too. – Alex W

Just put a div and update the content...

<script>
function setvideo(src) {
    document.getElementById('div_video').innerHTML = '<video autoplay controls id="video_ctrl" style="height: 100px; width: 100px;"><source src="'+src+'" type="video/mp4"></video>';
    document.getElementById('video_ctrl').play();
}
</script>
<button onClick="setvideo('video1.mp4');">Video1</button>
<div id="div_video"> </div>

I solved this with this simple method

function changeSource(url) {
   var video = document.getElementById('video');
   video.src = url;
   video.play();
}

I come with this to change video source dynamically. "canplay" event sometime doesn't fire in Firefox so i have added "loadedmetadata". Also i pause previous video if there is one...

var loadVideo = function(movieUrl) {
    console.log('loadVideo()');
    $videoLoading.show();
    var isReady = function (event) {
            console.log('video.isReady(event)', event.type);
            video.removeEventListener('canplay', isReady);
            video.removeEventListener('loadedmetadata', isReady);
            $videoLoading.hide();
            video.currentTime = 0;
            video.play();
        },
        whenPaused = function() {
            console.log('video.whenPaused()');
            video.removeEventListener('pause', whenPaused);
            video.addEventListener('canplay', isReady, false);
            video.addEventListener('loadedmetadata', isReady, false); // Sometimes Firefox don't trigger "canplay" event...
            video.src = movieUrl; // Change actual source
        };

    if (video.src && !video.paused) {
        video.addEventListener('pause', whenPaused, false);
        video.pause();
    }
    else whenPaused();
};
upvote
  flag
Yeah HTML5 video events in Firefox really need an upgrade ... – Arnaud Leyder
upvote
  flag
The fix, definitely worked for me, firefox is not handling property neither canplay nor canplaythrough and lodedmetadata needs to be added (and removed later) to the video handlers :-( – Lord of freaks

This is my solution:

<video id="playVideo" width="680" height="400" controls="controls">
    <source id="sourceVideo" src="{{video.videoHigh}}" type="video/mp4">
</video>
    <br />
<button class="btn btn-warning" id="{{video.videoHigh}}" onclick="changeSource(this)">HD</button>
<button class="btn btn-warning" id="{{video.videoLow}}" onclick="changeSource(this)">Regular</button>

<script type="text/javascript">
    var getVideo = document.getElementById("playVideo");
    var getSource = document.getElementById("sourceVideo");
    function changeSource(vid) {
        var geturl = vid.id;
        getSource .setAttribute("src", geturl);
        getVideo .load()
        getVideo .play();
        getVideo .volume = 0.5;
    }
</script>

Not the answer you're looking for? Browse other questions tagged or ask your own question.