Video med HTML5

Mine erfaringer med video i HTML5 har vært begrenset, jeg har som regel forholdt meg til plattformen Vimeo og i enkelte tilfeller YouTube.

Men da jeg for en snau uke siden skulle dele timelapse-prosjektet mitt her i denne bloggen valgte jeg å gå for video i HTML5 i stedet for embedding fra tjenester som Vimeo og YouTube.

Jeg hadde sannsynligvis ikke vært like dristig om dette var på nettstedet til noen andre, da HTML5 og video ikke er 100 % standard i alle browserversjoner. Embedding fra Vimeo eller YouTube ville ha løst eventuelle kompatibilitetsproblemer, med Flash og HTML5 om hverandre, avhengig av hva som ble støttet. Men siden denne bloggen er mest for de «spesielt innvidde» valgte jeg å kjøre en hard linje. Kan du ikke se videoene mine? Ikke mitt problem!

I all enkelhet

Jeg startet med en helt enkel og strippet ned video-tag for embedding av en MP4 videofil. Jeg kunne også hatt med formatene WebM og Ogg, men MP4 støttes av de fleste, så jeg valgte å gå for dette ene formatet.

<video  width="100%" controls>
<source src="/img/timelapse-linneslia.mp4" type="video/mp4">
<p>Nettleseren din st?tter tydeligvis ikke visning av MP4-video og HTML5</p>
</video>

Resultatet:

Dette resultatet er i og for seg ganske tilfredsstillende, men jeg liker ikke helt knappene i avspilleren, som for øvrig ser helt annerledes ut i de forskjellige nettleserne.

Tilpasset mitt eget design

Jeg laget derfor min egen videoavspiller med CSS og la til funksjonalitet med JavaScript:

HTML-koden jeg bruker når jeg skal legge inn en ny video i et blogginnlegg er så enkel som dette:

<p class="hardcodevideo" data-mp4-url="/img/timelapse-linneslia.mp4">

Fra placeholder til video-tag

Når siden er ferdig lastet putter jeg riktig markup inn i <class="hardcodevideo"> med jQuery. Og URL til videofilen får jeg fra data-attributtet «data-mp4-url».

Riktig markup sørger for at designet blir riktig på avspilleren, siden jeg har definert dette i CSS, blant annet ved hjelp av CSS sprites.

Jeg knytter flere «click events» til de forskjellige knappene/kontrollene, slik at play- og pauseknappen får riktig funksjonalitet. Her hadde jeg litt problemer når jeg forsøkte å kun bruke jQuery, det viste seg at det ikke var rett frem å gjøre kall til videoelementet fra jQuery, så jeg måtte gå den «tunge veien» om DOM (Document Object Model).

Neste steg var å vise en linje under videoen som viste hvor langt avspillingen var kommet. Det gjorde jeg enkelt ved å ha en <div> hvor bredden var definert i prosent, som tilsvarte samme prosent av video som var avspilt. Er 50 % av videolengden avspilt skal bredden være 50 %.

Verre var det ikke. Jeg gjengir både HTML, CSS og JavaScript nedenfor. Men husk at dette var noe jeg satte sammen i løpet av en kveld da jeg skulle vise en video i et blogginnlegg, så løsningen kan høyst sannsynlig forbedres.

HTML

<p class="hardcodevideo" data-mp4-url="/img/timelapse-linneslia.mp4">

Et problem jeg oppdaget i Chrome er at samme video-url ikke kan brukes i flere video-tagger på den samme siden, da vil bare én av de fungere. Så vidt meg bekjent finnes det ingen løsning på dette problemet, annet enn å legge til noen parametere i URL-en som gjør at Chrome behandler det som separate videofiler. Men da mister man også fordelene som caching gir, siden begge lastes som en egen fil. Et slikt parameter kan for eksempel være «videofil.mp4?i=1», «videofil.mp4?i=2» og så videre.

CSS

.hardcodevideo { background: #000; width: 100%; }
.hardcodevideo .spillknappoverlay { position: absolute; top: 0; left: -2000px; width: 128px; height: 128px; background-image: url(/style/playknapp_128x128.png); background-repeat: no-repeat; background-position: center center; opacity: 0.8; cursor: pointer; }
.hardcodevideo .spillknappoverlay:hover { opacity: 0.9; }
.hardcodevideo video { display: block; width: 100%; padding: 0; margin: 0; cursor: pointer; }
.hardcodevideo .fremdriftscontainer { background-image: url(/style/menu.png); background-repeat: repeat-x; background-position: bottom left; height: 10px; }
.hardcodevideo .fremdriftscontainer .fremdriftsindikator { background-image: url(/style/videofremdrift_bg.png); background-position: top right; height: 10px; width: 0%; }
.hardcodevideo .videokontrollere { background-image: url(/style/menu.png); background-repeat: repeat-x; color: white; height: 32px; padding: 5px; }
.hardcodevideo .videokontrollere .playknapp { opacity: 0.8; cursor: pointer; float: left; background-image: url(/style/playpause.png); background-position: 0px 0px; width: 32px; height: 32px; }
.hardcodevideo .videokontrollere .playknapp:hover { opacity: 1; }
.hardcodevideo .videokontrollere .tidspunkter { float: right; font-size: 0.8em; }

Her har jeg lagt opp til at alle videoer vises i 100 % bredde, dvs. 100 % bredde av området satt av til artikkel-/blogginnhold, noe som passer til mitt responsive design.

JavaScript

Forutsetter inkludering av jQuery-biblioteket.

var fremdriftsintervall = 0;

$( document ).ready(function() {
	$('.hardcodevideo').each(function(){
		id = Math.round((Math.random()*1000)+1000);
		mp4 = $(this).data('mp4-url');
		$(this).attr('id','videocontainer_'+id);
	  lagHardcodeVideoavspiller(id,mp4)
	});
});

function lagHardcodeVideoavspiller(id,mp4)
 {
 	$('#videocontainer_'+id).html('');
 	$('#videocontainer_'+id).append('<div class="spillknappoverlay"></div><video id="video_'+id+'"><source src="'+mp4+'" type="video/mp4"><p>Nettleseren din støtter tydeligvis ikke visning av MP4-video og HTML5</p></video>');
 	$('#videocontainer_'+id).append('<div class="fremdriftscontainer"><div class="fremdriftsindikator"></div></div>');
 	$('#videocontainer_'+id).append('<div class="videokontrollere"><div class="playknapp"></div><div class="tidspunkter"></div><div class="fullskjermknapp" style="display: none;">Fullskjerm</div></div>');
 	//$('#videocontainer_'+id).append('<input class="fullskjermknapp" type="button" value="fullscreen">');
 	
 	// play-pause knapp
	$('#videocontainer_'+id+' .videokontrollere .playknapp').click(function() {
	  videoPlayAndPause('videocontainer_'+id,'video_'+id)
	});
 	// play-pause knapp
	$('#videocontainer_'+id+' .spillknappoverlay').click(function() {
	  videoPlayAndPause('videocontainer_'+id,'video_'+id)
	});
	
	// selve videovinduet
	$('#video_'+id).click(function() {
	  videoPlayAndPause('videocontainer_'+id,'video_'+id)
	});
 	
 	// skipping
	$('#videocontainer_'+id+' .fremdriftscontainer').mousedown(function(e) {
	   fremdriftshopp('videocontainer_'+id,'video_'+id,e.pageX)
	});

	$('#videocontainer_'+id+' .fullskjermknapp').click(function() {
	   //For Webkit
	   document.getElementById('video_'+id).webkitEnterFullscreen();
	 
	   //For Firefox
	   document.getElementById('video_'+id).mozRequestFullScreen();
	 
	   return false;
	});

	// event video ferdig avspilt
	$('#video_'+id).bind("ended", function() {
		 clearInterval ( fremdriftsintervall );
	   $('#videocontainer_'+id+' .videokontrollere .playknapp').css("background-position", "0px 0px");
	   $('#videocontainer_'+id+' .fremdriftsindikator').css("width", "100%");
	   posisjonerplayknappen('videocontainer_'+id,'video_'+id);
	   $('#videocontainer_'+id+' .spillknappoverlay').show();
	});
	
	$(window).resize(function() {
	  posisjonerplayknappen('videocontainer_'+id,'video_'+id);
	});
	
	setTimeout(function(){posisjonerplayknappen('videocontainer_'+id,'video_'+id)},1000);
	setTimeout(function(){posisjonerplayknappen('videocontainer_'+id,'video_'+id)},3000);
 	
 }

function posisjonerplayknappen(containerid,videoid)
 {
 	var videoOffsetLeft = $('#'+videoid).offset().left;
 	var videoWidth = $('#'+videoid).width();
 	var videoOffsetTop = $('#'+videoid).offset().top;
 	var videoHeight = $('#'+videoid).height();
 	playknappLeft = videoOffsetLeft + ((videoWidth-128)/2);
 	playknappTop = videoOffsetTop + ((videoHeight-128)/2);
 	$('#'+containerid+' .spillknappoverlay').css('left', playknappLeft+'px');
 	$('#'+containerid+' .spillknappoverlay').css('top', playknappTop+'px');
 }



function fremdriftshopp(containerid,videoid,xposisjon)
 {
 	posisjonerplayknappen(containerid,videoid);
 	$('.fremdriftscontainer').offset().left;
 	hopptilprosent = (xposisjon - $('.fremdriftscontainer').offset().left) / $('.fremdriftscontainer').width();
 	hopptilsekund = document.getElementById(videoid).duration * hopptilprosent;
	$('#'+containerid+' .fremdriftsindikator').css("width", hopptilprosent+"%");
  document.getElementById(videoid).currentTime = hopptilsekund;
 }

function videofremdrift(containerid,videoid)
 {
 	var totallengde = document.getElementById(videoid).duration;
 	var avspiltlengde = document.getElementById(videoid).currentTime;
 	var prosentvisavspilt = ((avspiltlengde/totallengde)*100);
 	$('#'+containerid+' .fremdriftsindikator').css("width", prosentvisavspilt+"%");
 	$('#'+containerid+' .tidspunkter').html(baresekundertilminutterogsekunder(avspiltlengde)+' / '+baresekundertilminutterogsekunder(totallengde));
 }

function videoPlayAndPause(containerid,videoid)
 {
 	posisjonerplayknappen(containerid,videoid);
 	if($('#'+containerid+' .playknapp').css("background-position")=="0px 0px") // play er trykket
 	 {
 	 	fremdriftsintervall = setInterval ( "videofremdrift('"+containerid+"','"+videoid+"')", 50 );
 	 	document.getElementById(videoid).play();
 	 	$('#'+containerid+' .spillknappoverlay').hide();
 	 	$('#'+containerid+' .playknapp').css("background-position", "32px 0px");
 	 }
 	else // pause er trykket
 	 {
 	 	clearInterval ( fremdriftsintervall );
 	 	document.getElementById(videoid).pause();
 	 	$('#'+containerid+' .spillknappoverlay').show();
 	 	$('#'+containerid+' .playknapp').css("background-position", "0px 0px");
 	 }
 }

function baresekundertilminutterogsekunder(sekunder)
 {
 	if(sekunder>60) // vi har også minutter
 	 {
 	 	minutter = Math.floor(sekunder/60);
 	 	sekunder = sekunder - (minutter*60);
 	 }
 	else
 	 {
 	 	minutter = 0;
 	 }
 	sekunder = Math.floor(sekunder);
 	if(sekunder==60) // det tar seg dårlig ut med 60 sekunder
 	 {
 	 	sekunder = 59;
 	 }
 	if(sekunder<10)
 	 {
 	 	sekunder = '0'+sekunder;
 	 }
 	return minutter+':'+sekunder;
 }

Mulige forbedringer

  • Endring av volum
  • Fullskjerm (jeg har koden på plass, trenger bare en fullskjermsknapp)
  • Vise bufferstatus
  • Støtte for formatene WebM og Ogg
Svend Asbjørn Sylling, 14. desember 2013

Bloggen fra Sylling Hardcode