Duch Indiana Jonese: synchronizace videa s mapou

Velmi oblíbený efekt je zobrazování pozice na mapě spolu s videem – např. při jízdě nějakou trasou, kdy vidíme video zabírané z vozu a zároveň sledujeme pozici na mapě. V HTML5 můžeme použít tagu Video a synchronizovat zobrazování trasy na mapách s přehráváním pomocí zabudovaných API funkcí.

Článek je volným překladem textu Spirit of Indiana (Jones) – syncing HTML5 Video with Maps, jehož autorem je Chris Heilmann. Text vyšel na webu Mozilla Developer Center pod licencí CC-BY-SA. Tento text je k dispozici pod stejnou licencí, můžete jej tedy šířit a upravovat, pokud zachováte informace o autorovi a pokud své dílo zveřejníte pod podobnou licencí. 

Vždy se mi velmi líbily cestovatelské a letecké sekvence ve filmech o Indiana Jonesovi, a soudě dle velkého množství napodobenin na YouTube, nejsem sám. Nemám žádný software pro editování videa, tak jsem přemýšlel, zda by bylo možné dosáhnout téhož efektu za pomoci webových technologií a Google Maps, a výsledkem je toto:

Online demo zde

Můžete si stáhnout zdrojové kódy a vyzkoušet vše lokálně – budete potřebovat pouze prohlížeč, který zvládá HTML5 Video. Já vím – hudba není stejná jako ve filmech, ale ta, kterou jsem použil, aspoň není zatížena copyrightem a jde nám přímo od srdce (vznikla během pěti minut v zasedačce v kancelářích Mozilly).

Jak to celé funguje a jaké problémy bylo potřeba řešit? Čtěte dál.

Krok 1: najděte video a získejte ho ve správném formátu

To byla ta snazší část. Mnoho public domain videí lze nalézt na Archive.org, a navíc jsou už ve formátech, vhodných pro HTML5 video. V tomto případě jsem použil krátké video s Charlesem Lindberghem, pořízené při jeho rekordním letu z New Yorku do Paříže v roce 1927.

Krok 2: zobrazení videa

Použití videa je velmi jednoduché:

<div id="stage">
  <video>
    <source src="http://www.archive.org/download/
CharlesLindbergTakesOff/CharlesLindbergTakesOff_512kb.mp4"
type='video/mp4; codecs="avc1.42E01E, mp4a.40.2"'>
    <source src="http://www.archive.org/download/
CharlesLindbergTakesOff/CharlesLindbergTakesOff.ogv"
 type='video/ogg; codecs="theora, vorbis"'>
  </video>
</div>

Formát MP4 využijí prohlížeče založené na Webkitu, Ogg použije Firefox a další. Vynecháme atribut controls u elementu video, protože si budeme video spouštět ve vlastní režii. Takže si vytvoříme tlačítko, které spustí přehrávání JavaScriptem:

window.addEventListener('load',
  function() {
    var stage = document.getElementById('stage');
    var v = document.getElementsByTagName('video')[0];
    but = document.createElement('button');
    but.innerHTML = 'Click to see Lindbergh's flight';
    stage.appendChild(but);
    but.addEventListener('click',function(e) {
      v.play();
      e.preventDefault();
    },false);
  },
false);

Protože je video součástí značkovacího jazyka, nikoli přehrávané přes plugin, můžeme s ním dělat co chceme – výhoda otevřených technologií. Například můžeme, jako v tomto případě, nastavit průhlednost pomocí CSS a umístit jej přes mapu.

Krok 3: animace cesty na mapě

Pojďme se teď zaměřit na animaci trasy. Google Earth má na to API, ale potřebuje speciální plugin. Naštěstí Google Maps umožňují kreslit do mapy (pomocí SVG, což je další otevřený formát). Zabalíme toto malování do funkce, a získáme požadovaný efekt:

V podstatě jsem pouze vzal výchozí a koncový bod a spočítal jsem tolik mezihodnot, kolik jich je potřeba pro celou animaci. Uložil jsem si souřadnice do pole pos; následně zobrazuji cestu od začátku na současnou pozici a centruji mapu na aktuální bod při každé iteraci.

spirit.draw = function(){
  var path = new google.maps.Polyline({
        path: [startpos,pos[now]],
        strokeColor: "#c00",
        strokeOpacity: .7,
        strokeWeight: 10
  });
  path.setMap(map);
  map.panTo(pos[now])
  now = now + 1;
  if(now < animationend-1){
    setTimeout(spirit.draw,200);
  }
}

Detaily naleznete v komentovaném výpisu kódu. Teď můžeme použít tuto animaci a pustit video přes ni. Problém je, že animace a video mohou ztratit synchronizaci. Když se video zasekne (jak se často stává např. na hotelových WiFi), tak by bylo dobré animaci pozastavit.

Krok 4: synchronizace videa a pohybu mapy

Mít dva zdroje časového signálu není dobré, je na místě se rozhodnout, kterému dáme přednost. V našem případě to bude přehrávané video.

Mimochodem – možná jste si všimli, že jsem kód mapy zabalil do obsluhy události tilesloaded. To je další opatření pro udržení synchronizace. Zjistil jsem totiž, že na pomalejším připojení může nahrávání mapových podkladů brzdit celé rozhraní, proto je celé rozhraní závislé na načtení mapy a je spuštěné až ve chvíli, kdy jsou podklady načtené.Protože je obsluha tilesloaded vyvolávána i při pohybu mapy, potřebujeme příznak, kterým zabráníme opakování efektu:

google.maps.event.addListener(map,'tilesloaded',function(){
  if(played === false){
    // [...other code...]
    played = true;
  }
});

Aktuální časovou značku přehrávaného videa zjistíme z video.currentTime. Dokud se video přehrává, volá neustále obsluhu události timeupdate. Protože je takových událostí vyvoláno opravdu mnoho, musíme obsluhu trochu urychlit. Trik je v tom, že počítáme po sekundách a zvyšujeme počítadlo vždy, když je dosaženo nové sekundy. Můžete se podívat na ukázku timestamp a sekundového intervalu v demonstraci synchronizace:

var now = 0;
v.addEventListener('timeupdate',function(o){
  log.innerHTML = v.currentTime; /* logging the real timestamp */
  var full = parseInt(v.currentTime);
  if(full >= now) {
    seqlog.innerHTML = now;  /* logging the seconds firing */
    now = now + 1;
  }
},false);

V tomto případě se může přehrávání videa přerušit kvůli přerušení datového toku, a sekvence stále zůstane synchronizovaná. Viz zdrojový kód na Githubu.

Dáme to vše dohromady

Není třeba už mnoho práce – jediné, co jsem musel udělat, bylo nastavit průhlednost videa v určitou chvíli, v jinou spustit zvuk a v dalších bodech ukázat a zase schovat copyright. Protože používáme obsluhu události společně s ostatními efekty, potřebujeme opět příznaky, které zajistí, že se činnost nevyvolá vícekrát:

v.addEventListener('timeupdate',function(o){
  full = parseInt(v.currentTime);
  if(full === now-1){
    mapelm.style.opacity = .8;
    v.style.opacity = .4;
  }
  if(full === animationstart+1 && audioplay === false){
    a.play();
    audioplay = true;
  }
  if(full === animationstart+2 && hidden === true){
    drmbedamned.style.display = 'block';
    hidden = false;
  }
  if(full === animationstart+3 && hidden === false){
    drmbedamned.style.display = 'none';
    hidden = true;
  }
  if(full >= now) {
    path = new google.maps.Polyline({
        path: [startpos,pos[full]],
        strokeColor: "#c00",
        strokeOpacity: .7,
        strokeWeight: 10
    });
    path.setMap(map);
    map.panTo(pos[full])
    now = now + 1;
  }
},false);

Další událostí, kterou si obsloužíme, je konec přehrávaného videa. V tu chvíli zastavíme zvuk a pustíme rolující titulky:

v.addEventListener('ended',function(o){
  a.pause();
  spirit.credslist.parentNode.style.display = 'block';
  spirit.creds();
},false)

Protože je audio příliš krátké na to, aby vystačilo po celou dobu animace, potřebujeme jej „zacyklit“. To uděláme testem na událost  ended a „převinutím“ času zpět na 0:

a.addEventListener('ended', function(o) {
  a.currentTime = 0;
},false);

Souhrn

A máme hotovo – mapa ve stylu Indiana Jonese pomocí otevřených technologií. Včetně náhradního řešení pro audio (bylo nahráno, stříháno a konvertováno ve volném editoru Audacity) a s využitím Google’s Web Fonts pro grafiku.

Můžete toto demo dál upravovat, třeba:

  • Nahradit Google Maps pomocí Openstreetmap, kvůli omezením GMaps
  • Udělat cestu z NYC do Paříže jako lehký oblouk, aby byla věrnější (ale opět – časování není stoprocentní, protože Lindberghovi to trvalo přece jen o něco delší dobu)
  • Použít místo mapy obyčejný obrázek a malovat do něj v Canvasu, čímž zrychlíte a zjemníte animaci.

Proč to nezkusit? Je to volně k použití, a je to zábava!

Začal programovat v roce 1984 s programovatelnou kalkulačkou. Pokračoval k BASICu, assembleru Z80, Forthu, Pascalu, Céčku, dalším assemblerům, před časem v PHP a teď by rád neprogramoval a radši se věnoval starým počítačům.

Věděli jste, že nám můžete zasílat zprávičky? (Jen pro přihlášené.)

Komentáře: 3

Přehled komentářů

Tomáš Dá se s tím udělat mapa vedle videa?
Martin Malý Re: Dá se s tím udělat mapa vedle videa?
Simca Cesta s videem
Zdroj: https://www.zdrojak.cz/?p=3402