-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Enrique Barra
committed
Mar 18, 2013
1 parent
fc867c2
commit 6e14539
Showing
9 changed files
with
2,573 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
body { | ||
min-width: 836px; | ||
} | ||
#video { | ||
min-width: 455px; | ||
padding: 0; | ||
margin: 0; | ||
} | ||
#controls { | ||
margin-top:5px; | ||
} | ||
#button { | ||
display: inline; | ||
} | ||
#play { | ||
background:url(../images/buttons.png) no-repeat top left; | ||
border:none; | ||
height: 30px; | ||
width: 30px; | ||
padding: 5px; | ||
float: left; | ||
display: inline-block; | ||
} | ||
#positionview { | ||
float: left; | ||
width: 80%; | ||
display: inline; | ||
} | ||
#transportbar { | ||
height: 15px; | ||
width: 100%; | ||
border: 2px solid black; | ||
} | ||
#position { | ||
background: #D7BC28; | ||
height: 15px; | ||
width: 0px; | ||
display: block; | ||
} | ||
#segments { | ||
width: 100%; | ||
margin: 0; | ||
padding: 0; | ||
border: 2px solid; | ||
border-top: none; | ||
position: relative; | ||
background: none; | ||
height: 10px; | ||
list-style-type: none; | ||
} | ||
.segment { | ||
border-right: 1px solid; | ||
background: none; | ||
height: 10px; | ||
float: left; | ||
cursor: pointer; | ||
position: absolute; | ||
} | ||
.selector { | ||
width: 100%; | ||
height: 100%; | ||
} | ||
#time { | ||
position: relative; | ||
float: right; | ||
} | ||
#keys { | ||
list-style-type: none; | ||
} | ||
#keys span { | ||
text-transform: uppercase; | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
|
||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<title>Navigation using WebVTT</title> | ||
<link href="css/navigation.css" type="text/css" rel="stylesheet"/> | ||
</head> | ||
<body> | ||
<div role="application"> | ||
<div id="videoBox" style="float:left; width:58%;"> | ||
<h4>HTML5 video accessibility and the WebVTT file format</h4> | ||
<video poster="videos/webvtt_talk.png" style="width:100%" preload="metadata"> | ||
<source src="videos/webvtt_talk.webm"> | ||
<source src="videos/webvtt_talk.mp4"> | ||
<track id="nav" src="videos/webvtt_talk_navigation.vtt" kind="chapters" srclang="en"></track> | ||
<track id="cc" src="videos/webvtt_talk_captions.vtt" kind="captions" label="captions" srclang="en" default></track> | ||
</video> | ||
<div id="controls"> | ||
<div id="button"><input id="play" type="image" src="images/0.gif" alt="Play video"></div> | ||
<div id="positionview"> | ||
<div id="transportbar"> | ||
<div id="position"></div> | ||
</div> | ||
<ul id="segments" title="chapter navigation" aria-describedby="keys"></ul> | ||
</div> | ||
<div id="time"> | ||
<span id="curTime">00:00</span>/<span id="duration">00:00</span> | ||
</div> | ||
</div> | ||
<div style="display: block; clear: both;"></div> | ||
<ul id="keys"> | ||
<li><span>space</span> = play / pause toggle</li> | ||
<li><span>enter</span> = navigate to chapter</li> | ||
<li><span>tab</span> = navigate chapters</li> | ||
<li><span>ctl-alt-downarrow</span> = navigate text elements</li> | ||
</ul> | ||
</div> | ||
<div id="transcriptBox" style="width:40%; float:right;"> | ||
<h4>Navigation to specific time points.</h4> | ||
<p style="font-size:small;"> | ||
Click on link to navigate to video fragment. Press space to toggle video play/pause. | ||
</p> | ||
<div id="navigation" | ||
style="padding:5px; background-color:#FAF9F8; border:2px black solid;"> | ||
<ul id="chapters"> | ||
</ul> | ||
</div> | ||
</div> | ||
<script type="text/javascript" src="js/navigation.js"></script> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,247 @@ | ||
var video, track; | ||
var cues = []; | ||
var xhr; | ||
|
||
// get video element, track, and duration element | ||
video = document.getElementsByTagName('video')[0]; | ||
track = video.querySelectorAll('track')[0]; | ||
duration = document.getElementById('duration'); | ||
|
||
// display duration and chapters once video is loaded | ||
video.addEventListener("loadedmetadata", init, false); | ||
if (video.readyState >= video.HAVE_METADATA) { | ||
init.apply(video); // missed the event | ||
} | ||
|
||
// update transport bar while playing | ||
position = document.getElementById('position'); | ||
curTime = document.getElementById('curTime'); | ||
video.addEventListener("timeupdate", curTimeUpdate, false); | ||
|
||
// play/pause button | ||
play = document.getElementById('play'); | ||
play.addEventListener('click', togglePlay, false); | ||
|
||
// click on transport bar sets playback position | ||
transportbar = document.getElementById('transportbar'); | ||
transportbar.addEventListener("click", seek, false); | ||
|
||
// pause video when current chapter is finished | ||
video.addEventListener("timeupdate", endChapter, false); | ||
|
||
|
||
// display duration and chapters | ||
function init(evt) { | ||
// update duration display | ||
duration.innerHTML = video.duration.toFixed(2); | ||
|
||
// grab chapters out of <track> | ||
retrieve(track.getAttribute('src')); | ||
|
||
// display chapters in list and transport bar | ||
displayChapters(); | ||
paintChapterbar(); | ||
} | ||
|
||
// pause/play button | ||
function togglePlay() { | ||
if (video.paused == false) { | ||
video.pause(); | ||
play.style.backgroundPosition = '0 0'; | ||
} else { | ||
video.play(); | ||
play.style.backgroundPosition = '0 -75px'; | ||
} | ||
} | ||
|
||
// capture onkeydown on the navigation to allow space bar to toggle play/pause | ||
function videoPlayPause(evt) { | ||
if (evt.keyCode == "32") { // space bar | ||
togglePlay(); | ||
} | ||
if (evt.keyCode == "13") { // enter key | ||
seekChapter(this.getAttribute('data-chapter')); | ||
// stop event from bubbling | ||
evt = evt||event; /* get IE event ( not passed ) */ | ||
evt.stopPropagation? evt.stopPropagation() : evt.cancelBubble = true; | ||
} | ||
} | ||
|
||
// update transport bar time display | ||
function curTimeUpdate(evt) { | ||
var bar_width = document.getElementById('positionview').offsetWidth; | ||
curTime.innerHTML = video.currentTime.toFixed(2); | ||
position.style.width = Math.round(bar_width*video.currentTime/video.duration) + "px"; | ||
} | ||
|
||
// seek on transport bar | ||
function seek(evt) { | ||
var bar_width = document.getElementById('positionview').offsetWidth; | ||
var clickpos = evt.pageX - this.offsetLeft; | ||
var clickpct = clickpos / bar_width; | ||
video.currentTime = clickpct * video.duration; | ||
// clear chapter selection | ||
for (i = 0; i < cues.length; i++) { | ||
var segid = "segment" + i; | ||
var segment = document.getElementById(segid); | ||
segment.style.backgroundColor = ""; | ||
} | ||
} | ||
|
||
// seek chapters | ||
function seekChapter(chapter) { | ||
if (chapter) { | ||
video.currentTime = parseFloat(cues[chapter].start); | ||
for (i = 0; i < cues.length; i++) { | ||
var segid = "segment" + i; | ||
var segment = document.getElementById(segid); | ||
if (i == parseInt(chapter)) { | ||
segment.style.backgroundColor = "green"; | ||
} else { | ||
segment.style.backgroundColor = ""; | ||
} | ||
} | ||
} | ||
} | ||
|
||
// pause on chapter end | ||
function endChapter(evt) { | ||
var curChapter = video.getAttribute('data-chapter'); | ||
if (video.currentTime >= cues[curChapter] && !video.paused) togglePlay(); | ||
} | ||
|
||
// retrieve chapters via xhr and process them | ||
function retrieve(url) { | ||
xhr = new XMLHttpRequest(); | ||
if (xhr != null) { | ||
xhr.open("GET", url, false /* sync */); | ||
xhr.setRequestHeader('Content-Type', 'text/text; charset=utf-8'); | ||
|
||
xhr.onreadystatechange = function() { | ||
if (xhr.readyState == 4 /* complete */) { | ||
if (xhr.status != 200) { | ||
alert('Unable to retrieve file.'); | ||
} else { | ||
cues = parseWebVTT(xhr.responseText); | ||
} | ||
} | ||
} | ||
xhr.send(); | ||
} else { | ||
alert('Error retrieving file.'); | ||
} | ||
} | ||
|
||
// display chapter list on screen | ||
function displayChapters() { | ||
// create navigation list on right | ||
var chapters = document.getElementById('chapters'); | ||
for (i=0; i < cues.length; i++) { | ||
var item = document.createElement('li'); | ||
item.id = cues[i].id; | ||
var link = document.createElement('a'); | ||
link.href = '#videoBox'; | ||
link.innerHTML = cues[i].id + ': ' + cues[i].content; | ||
link.setAttribute('data-chapter', i); | ||
link.addEventListener('keydown', videoPlayPause, false); | ||
link.onclick = function () { | ||
seekChapter(this.getAttribute('data-chapter')); | ||
} | ||
item.appendChild(link); | ||
chapters.appendChild(item); | ||
} | ||
} | ||
|
||
// paint chapters into chapter bar | ||
function paintChapterbar() { | ||
// create time segments under transportbar | ||
var duration = parseFloat(video.duration); | ||
var segments = document.getElementById('segments'); | ||
var bar_width = document.getElementById('positionview').offsetWidth; | ||
// TODO: bug - the offsetWidth is unknown at load state | ||
var_width = 558; | ||
|
||
var perc = bar_width / duration; | ||
|
||
for (i=0; i < cues.length && duration > 0; i++) { | ||
var segment = document.createElement('li'); | ||
var start = parseFloat(cues[i].start); | ||
var end = parseFloat(cues[i].end); | ||
segment.id = "segment" + i; | ||
segment.className = "segment"; | ||
segment.title = cues[i].content + "\npress enter to navigate, space to toggle play"; | ||
segment.style.left = Math.round((start * perc) - 0.5) + 'px'; | ||
segment.style.width = Math.round((end - start) * perc) + 'px'; | ||
segment.setAttribute('data-chapter', i); | ||
segment.setAttribute('tabindex', '0'); | ||
segment.setAttribute('role', 'button'); | ||
segment.onclick = function () { | ||
seekChapter(this.getAttribute('data-chapter')); | ||
} | ||
segment.addEventListener('keydown', videoPlayPause, false); | ||
segments.appendChild(segment); | ||
} | ||
} | ||
|
||
// Function to parse webvtt file | ||
function parseWebVTT(data) { | ||
var srt; | ||
// check WEBVTT identifier | ||
if (data.substring(0,6) != "WEBVTT") { | ||
alert("Missing WEBVTT header: Not a WebVTT file - trying SRT."); | ||
srt = data; | ||
} else { | ||
// remove WEBVTT identifier line | ||
srt = data.split('\n').slice(1).join('\n'); | ||
} | ||
|
||
// clean up string a bit | ||
srt = srt.replace(/\r+/g, ''); // remove dos newlines | ||
srt = srt.replace(/^\s+|\s+$/g, ''); // trim white space start and end | ||
|
||
// srt = srt.replace(/<[a-zA-Z\/][^>]*>/g, ''); // remove all html tags for security reasons | ||
|
||
// parse cues | ||
var cuelist = srt.split('\n\n'); | ||
for (i = 0; i < cuelist.length; i++) { | ||
var cue = cuelist[i]; | ||
var content = "", start, end, id = ""; | ||
var s = cue.split(/\n/); | ||
var t = 0; | ||
// is there a cue identifier present? | ||
if (!s[t].match(/(\d+):(\d+):(\d+)/)) { | ||
// cue identifier present | ||
id = s[0]; | ||
t = 1; | ||
} | ||
// is the next line the time string | ||
if (!s[t].match(/(\d+):(\d+):(\d+)/)) { | ||
// file format error: next cue | ||
continue; | ||
} | ||
// parse time string | ||
var m = s[t].match(/(\d+):(\d+):(\d+)(?:.(\d+))?\s*--?>\s*(\d+):(\d+):(\d+)(?:.(\d+))?/); | ||
if (m) { | ||
start = | ||
(parseInt(m[1], 10) * 60 * 60) + | ||
(parseInt(m[2], 10) * 60) + | ||
(parseInt(m[3], 10)) + | ||
(parseInt(m[4], 10) / 1000); | ||
end = | ||
(parseInt(m[5], 10) * 60 * 60) + | ||
(parseInt(m[6], 10) * 60) + | ||
(parseInt(m[7], 10)) + | ||
(parseInt(m[8], 10) / 1000); | ||
} else { | ||
// Unrecognized timestring: next cue | ||
continue; | ||
} | ||
|
||
// concatenate text lines to html text | ||
content = s.slice(t+1).join("<br>"); | ||
|
||
// add parsed cue | ||
cues.push({id: id, start: start, end: end, content: content}); | ||
} | ||
return cues; | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Oops, something went wrong.