Digithoughts

HTML5 Video & Skinning Tutorial Part 5: Sound

June 28, 2010 by Nicholas Davison

Last Thursday we covered interactive progress bars.

Today we will be covering sound – muting, un-muting and adjusting the volume of our videos.

Throughout these tutorials, we will be continuing the concepts/implementation division: Explaining the ideas first, letting those who wish to run ahead do so – then going over a step by step implantation with jQuery.

Concepts

Property: volume

The .volume property is a floating point value from 0 to 1 that can be both read to find the current value for the player and also written to.

/* Get volume and store it */

var currentVolume=document.getElementById('myVideo').volume;

/* Get volume as a percentage and store it */

var currentVolumePercent=document.getElementById('myVideo').volume*100;

/* Set volume to 50% */

document.getElementById('myVideo').volume=0.5;

Property: muted

.volume alone doesn’t tell you everything. The Boolean .muted, also readable and writeable, controls whether the player is muted, regardless of what the volume is set to.

/* Get muted state and store it */

var isMuted=document.getElementById('myVideo').muted;

/* Mute */

document.getElementById('myVideo').muted=true;

/* Unmute */

document.getElementById('myVideo').muted=false;

Event: volumechange

To detect changes in volume or muting from other sources – such as user interaction via right clicks – you can listen for the volumechange event and then query .volume and .muted.

Implementation

HTML

We start by adding another button, just like all the others.

<li class="video_sound">

<a href="#">Sound</a>

</li>

And then, using it as an anchor point, add a pair of nested divs to act as the volume controls.

<li class="video_sound">

<a href="#">Sound</a>

      <div class="video_sound_volume">

            <div class="video_sound_volume_bar"></div>

      </div>

</li>

CSS

To make further absolute positioning use it as a 0,0 point, we make .video_sound position relative:

.video_sound {

      position: relative;

}

We make the .video_sound_volume an absolute positioned, initially hidden (display: none) container.

.video_sound .video_sound_volume {

      background-color: black;

      border: 1px inset #888;

      display: none;

      height: 100px;

      left: 25px;

      position: absolute;

      top: -95px;

      width: 52px;

Hovering over the parent .video_sound is then set to display .video_sound_volume.

For backwards compatibility with IE6, we would normally use the suckerfish approach. However, as this is for HTML5 video, which IE6 isn’t going to come close to supporting, I’ve left it out.

.video_sound:hover .video_sound_volume {

      display: block;

}

Finally, we add the volume bar itself, functioning exactly the same way the progress bar did earlier in the week, just vertically instead of horizontally.

.video_sound .video_sound_volume_bar {

      background-color: #00ff00;

      height: 50%;

      left: 0;

      position: absolute;

      bottom: 0;

      width: 100%;

}

JavaScript

As usual, we start by setting up the general framework we are going to fill in:

function initVideoSound() {

      /* For all videos */

      $('video').each(function() {

            /* Store local references to the controls to avoid having to repeatedly search */

            /* Bind the volumechange event for the video */

                       

            /* Bind the click event for the sound buttons to mute/unmute */

                       

            /* Set the initial values */

      });

     

      /* For all video sound volumes */

      $('.video_sound_volume').each(function() {

            /* Bind click functionality */

      });

}

 

 

Next we store all of the local references to avoid having to repeatedly search:

/* Store local references to the controls to avoid having to repeatedly search */

var _video=this;

var $_controls=findControlsForVideo(_video);

var $_volume_bars=$_controls.find('.video_sound_volume_bar');

var $_sound_buttons=$_controls.find('.video_sound a');

Next we intercept the volume change events from the video, updating the bar position:

/* Bind the volumechange event for the video */

$(_video).bind('volumechange', function() {

      /* Set the height of the volume bars to match the new volume */

      $_volume_bars.css("height", (_video.volume*100)+"%");

  

      if (_video.muted) {

            $_sound_buttons.removeClass('active');

      } else {

            $_sound_buttons.addClass('active');

      }

});

Then we do the same for the mute/unmute state of the sound button:

/* Bind the click event for the sound buttons to mute/unmute */

$_sound_buttons.bind('click', function(e) {

      e.preventDefault();

      if (_video.muted) {

            _video.muted=false;

      } else {

            _video.muted=true;

      }

});

And we set the initial states of the volume slide and mute buttons:

/* Set the initial values */

$_volume_bars.css("height", (_video.volume*100)+"%");

if (_video.muted) {

      $_sound_buttons.removeClass('active');

} else {

      $_sound_buttons.addClass('active');

}

Finally, in much the same way we did with the progress slider, we bind click interaction for the volume sliders:

/* For all video sound volumes */

$('.video_sound_volume').each(function() {

      /* Store local references to the video to avoid having to repeatedly search */

      var $_video=findVideoForElement(this);     

      /* Bind click functionality */

      $(this).click(function(e) {

            /* Calculate the decimal percentage (0-1.0) of the way up the bar the event happened at

               then invert it as the bar is relative to the bottom, not the top. */

            var _position=findEventPositionWithinElement(e);

            var _decimal=1-(_position.top/$(this).height());


            /* For all of the videos */

            $_video.each(function(index, _video) {

                  /* Set the new volume */

                  _video.volume=_decimal;

            });

      });

});

This gives us a complete initSound() function of:

function initVideoSound() {

      /* For all videos */

      $('video').each(function() {

            /* Store local references to the controls to avoid having to repeatedly search */

            var _video=this;

            var $_controls=findControlsForVideo(_video);

            var $_volume_bars=$_controls.find('.video_sound_volume_bar');

            var $_sound_buttons=$_controls.find('.video_sound a');

            /* Bind the volumechange event for the video */

            $(_video).bind('volumechange', function() {

                  /* Set the height of the volume bars to match the new volume */

                  $_volume_bars.css("height", (_video.volume*100)+"%");

                  if (_video.muted) {

                        $_sound_buttons.removeClass('active');

                  } else {

                        $_sound_buttons.addClass('active');

                  }

            });

          

            /* Bind the click event for the sound buttons to mute/unmute */

            $_sound_buttons.bind('click', function(e) {

                  e.preventDefault();

                  if (_video.muted) {

                        _video.muted=false;

                  } else {

                        _video.muted=true;

                  }

            });

           

            /* Set the initial values */

            $_volume_bars.css("height", (_video.volume*100)+"%");

           

            if (_video.muted) {

                  $_sound_buttons.removeClass('active');

            } else {

                  $_sound_buttons.addClass('active');

            }

      });     

      /* For all video sound volumes */

      $('.video_sound_volume').each(function() {

            /* Store local references to the video to avoid having to repeatedly search */

            var $_video=findVideoForElement(this);

           

            /* Bind click functionality */

            $(this).click(function(e) {

                  /* Calculate the decimal percentage (0-1.0) of the way up the bar the event happened at

                     then invert it as the bar is relative to the bottom, not the top. */

                  var _position=findEventPositionWithinElement(e);

                  var _decimal=1-(_position.top/$(this).height());                                   

                  /* For all of the videos */

                  $_video.each(function(index, _video) {

                        /* Set the new volume */

                        _video.volume=_decimal;

                  });

            });

      });

}

Resources

The W3C spec for HTML5 video is available at:

http://www.w3.org/TR/html5/video.html

However, we would recommend the whatwg version as its linked table of contents makes it slightly more usable (even if the text is just as dense):

http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html

 

 

Comments

Home Security Monitoring Mar 30, 2011 at 9:23pm

I've been absent for a while, but now I remember why I used to love this web site. Thank you, I will try and check back more often. How frequently you update your web site' Home Security Monitoring

phyprospada Jan 23, 2011 at 1:07pm

I love www.digitaria.com! Here I always find a lot of helpful information for myself. Thanks you for your work. Webmaster of http://loveepicentre.com and http://movieszone.eu Best regards

Post new comment

The content of this field is kept private and will not be shown publicly.