EasyCaptions

A simple system for adding captions and an interactive transcript to online videos. EasyCaptions uses progressive enhancement, providing the best possible experience for all visitors. Background information

Documentation

EasyCaptions API

Options:

  • enableTranscript (boolean; default is true)
    • If set to false, EasyCaptions will NOT make the transcript clickable.
  • enableCaptions (boolean; default is true)
    • If set to false, EasyCaptions will NOT dynamically generate the caption <div>
  • transcriptElementID (string or HTML DOM element)
    • Required. The HTML element containing the transcript. The transcript must be marked up with <span> elements using the custom data-begin attribute (data-end is optional).
  • videoElementID (string or HTML DOM element)
    • The HTML5 video element. If this element is not supplied the fallback will still function (assuming it is configured properly).
  • transcriptEnabledClass (string, default is “EasyCaptions-enabled”)
    • The class to append to the transcript text container. If no custom class name is provided by the user, the default “EasyCaptions-enabled” will be used
  • captionID (string, default is “EasyCaptions-caption”)
    • The ID to append to the generated caption area. If no custom ID is provided by the user, the default “EasyCaptions-caption” will be used



Returns: object with the following properties and methods

  • video_element (HTML DOM element)
    • The video element specified by the videoElementID option
  • transcript_element (HTML DOM element)
    • The transcript element specified by the transcriptElementID option
  • caption_element (HTML DOM element)
    • The caption element generated by EasyCaptions
  • html5_supported (boolean)
    • Indicates whether HTML5 video is supported in the user’s browser. Important note: This detection ONLY checks support for mime types specified in the markup. For example, if you only specify <source src="myvideo.mp4" type="video/mp4" />, the detection script will only check for video/mp4 support and will NOT check for any other mime types.
  • updateCaption(timecode) (function)
    • A utility that allows you to display the caption for a specific timecode. This is useful if you need to manually control caption timing. Pass the specified timecode (number representing seconds) as the argument.
  • enableFallback(obj) (function)
    • A utility that allows you to specify your own custom fallback mechanism (normally a Flash-based fallback, but other technologies such as Silverlight could be used as well). Accepts a single object argument, with the following properties:
      • elementID (string or HTML DOM element)
        • The element containing your fallback video (such as the <object> that loads the Flash-based JW Media Player)
      • captionHandler (function)
        • The hook for your fallback’s caption handling.
      • transcriptHandler (function)
        • The hook for your fallback’s transcript/onclick handling.

Code examples

Simplest example (no fallback):


<div id="transcript">
    <p>
        <span data-begin="0">My first phrase,</span> 
        <span data-begin="14">then a second.</span> 
        <span data-begin="16">A third comes shortly after.</span> 
    </p>
</div>

<video id="myVideoID" width="420" height="240" controls>
    <source src="myvideo.mp4" type="video/mp4" />
    <source src="myvideo.ogv" type="video/ogg" />
        <!-- fallback content here -->        
</video>
var easy = new EasyCaptions({
    videoElementID: "myVideoID",
    transcriptElementID: "transcript"
});

alert(easy.html5_supported); //returns true in modern browsers

With fallback:


<div id="transcript">
    <p>
        <span data-begin="0">My first phrase,</span> 
        <span data-begin="14">then a second.</span> 
        <span data-begin="16">A third comes shortly after.</span> 
    </p>
</div>

<video id="myVideoID" width="420" height="240" controls>
    <source src="myvideo.mp4" type="video/mp4" />
    <source src="myvideo.ogv" type="video/ogg" />
        
    <object id="video-swf" name="video-swf" data="player.swf" type="application/x-shockwave-flash" width="420" height="240">  
        <param value="player.swf" name="movie"/>  
        <param value="controlbar=over&file=myvideo.f4v" name="flashvars"/>
        
        <!-- fallback content here -->
        
    </object>    
        
</video>

var easy = new EasyCaptions({
    videoElementID: "myVideoID",
    transcriptElementID: "transcript"
});

easy.addFallback({

    elementID: "video-swf",

    captionHandler: function (video, updateCaption){
        //Nothing needed here; we're using 
        //JW Media Player's "playerReady" function
    },

    transcriptHandler: function (video, position){
        video.sendEvent('SEEK', position);
    }

});

//The following code is specific to JW Media Player.
//You could replace with something else if you
//prefer other Flash video players.	
jw_timeupdateHandler = function (video){
    easy.updateCaption(parseInt(video.position, 10));
};


Demonstrations

 

These demos have been successfully tested in the following browsers:

  • Firefox 3.0 (OSX 10.6.2, Windows XP)
  • Firefox 3.5 (OSX 10.6.2)
  • Firefox 3.6 (OSX 10.6.2, Windows 7)
  • Safari 4.0 (OSX 10.6.2)
  • Google Chrome 5.0 (OSX 10.6.2, Windows XP)
  • Opera 10.1, Opera 10.5 (OSX 10.6.2)
  • Internet Explorer 6 (Windows XP)
    • Issue: SWFObject demo doesn’t correctly remove fallback content
  • Internet Explorer 8 (Windows 7)
    • Issue: SWFObject demo doesn’t correctly remove fallback content

Download

Now hosted on GitHub

33 Replies to “EasyCaptions”

  1. Awesome!! This is perfect for a project I’m working on.

    Question: Is there a way to link to specific time within the ogg, Creating an index of the movie, in html5?

  2. The transcript feature can do that for you. Just use index text instead of caption text, such as

    
    
    1. Introduction
    2. Scene One
    3. Scene Two

    The EasyCaptions code should then point to the div containing your index

    var easy = new EasyCaptions({
        videoElementID: "myVideoID",
        transcriptElementID: "index",
        enableCaptions: false
    });
    

    If you want to do it without using EasyCaptions, I suggest reading up on HTML5 video’s currentTime property

  3. I’m doing something wrong as I cannot get the video to play in Firefox or IE (All good in Chrome, Opera, and Safari). When I add the second var is when I seem to start having problems. If you have any suggestions I would really appreciate it. Thank you so much!

    
    /* --- For JW Media Player's caption handling ---
    The following code is specific to JW Media Player.
    You could replace with something else if you
    prefer other Flash video players.
    */
    function playerReady(obj){
    	document.getElementById(obj.id).addModelListener("TIME", "jw_timeupdateHandler");
    }
    
    var jw_timeupdateHandler;
    /* --- END JW Media Player code --- */
    
    
    window.onload = function (){
    
    	var easy = new EasyCaptions({
    		videoElementID: "video-html5",
    		transcriptElementID: "transcript",
    		transcriptEnabledClass: "enabled"
    	});
    
    	var easy = new EasyCaptions({
    		videoElementID: "video-html5",
    		transcriptElementID: "cues",
    		transcriptEnabledClass: false
    	});	
    	
    	easy.addFallback({
    		 
    		 elementID: "video-swf",
    		 
    		 captionHandler: function (video, updateCaption){
    			//Nothing needed here; we're using 
    			//JW Media Player's "playerReady" function
    		 },
    		 
    		 transcriptHandler: function (video, position){
    			 
    			//The following code is specific to JW Media Player.
    			//You could replace with something else if you
    			//prefer other Flash video players.
    			
    			//Ensure video is playing before trying to SEEK
    			if(video.getConfig().state !== "PLAYING"){
    				video.sendEvent("PLAY", "true");				
    			}
    
    			video.sendEvent('SEEK', position);
    			
    		 }
    		 
    	});
    
    
    	//Set up Flash fallback handler. 
    	//The following code is specific to JW Media Player.
    	//You could replace with something else if you
    	//prefer other Flash video players.	
    	jw_timeupdateHandler = function (video){
    		easy.updateCaption(parseInt(video.position, 10));
    	};
    	/*	Progressive enhancement BONUS: 
    		Add notice to transcript letting people know it's clickable  */
    		
    	if(easy.transcript_element){	
    		var target_el = easy.transcript_element.getElementsByTagName("h3")[0].nextSibling;
    		var new_el = document.createElement("h4");
    		new_el.innerHTML = "The following transcript is clickable. Click on any sentence to jump to that point in the video.";
    		easy.transcript_element.insertBefore(new_el, target_el);
    	}
    
     
    
    };
    
    

    Interview

    TITLE
    Introduction to interview……….. Dean Ornish shares new research that shows how adopting healthy lifestyle habits can affect a person at a genetic level. For instance, he says, whIn this a en you live healthier, eat better, exercise, and love more, your brain cells actually increase.

    Click to section:

    Inroductions
    Section 1
    Section 2
    Section 3

    Transcript:

    One way to change our genes is to make new ones,
    as Craig Venter has so elegantly shown.
    Another is to change our lifestyles.
    And what we’re learning is how powerful and dynamic these changes can be,
    that you don’t have to wait very long to see the benefits.
    When you eat healthier, manage stress, exercise and love more,
    your brain actually gets more blood flow and more oxygen.
    But more than that, your brain gets measurably bigger.
    Things that were thought impossible just a few years ago
    can actually be measured now.
    This was figured out by Robin Williams
    a few years before the rest of us.

    Now, there’s some things that you can do
    to make your brain grow new brain cells.
    Some of my favorite things, like chocolate and tea, blueberries,
    alcohol in moderation, stress management
    and canabanoids found in marijuana.
    I’m just the messenger.
    (Laughter)
    What were we just talking about?
    (Laughter)
    And other things that can make it worse,
    that can cause you to lose brain cells.
    The usual suspects, like saturated fat and sugar,
    nicotine, opiates, cocaine, too much alcohol and chronic stress.

    Your skin gets more blood flow when you change your lifestyle,
    so you age less quickly, your skin doesn’t wrinkle as much.
    Your heart gets more blood flow.
    We’ve shown that you can actually reverse heart disease.
    That these clogged arteries that you see on the upper left,
    after only a year become measurably less clogged.
    And the cardiac PET scan shown on the lower left,
    the blue means no blood flow.
    A year later — orange and white is maximum blood flow.
    We’ve shown you may be able to stop and reverse the progression
    of early prostate cancer and, by extension, breast cancer,
    simply by making these changes.
    We’ve found that tumor growth in vitro was inhibited
    70 percent in the group that made these changes,
    whereas only nine percent in the comparison group.

    These differences were highly significant.
    Even your sexual organs get more blood flow,
    so you increase sexual potency.
    One of the most effective anti-smoking ads was done
    by the Department of Health Services,
    showing that nicotine, which constricts your arteries,
    can cause a heart attack or a stroke,
    but it also causes impotence.
    Half of guys who smoke are impotent.
    How sexy is that?

    Now we’re also about to publish a study —
    a study showing you can change gene expression in men with prostate cancer
    This is what’s called a heat map
    and the different colors, and along the side on the right are different genes
    And we found that over 500 genes were favorably changed
    in effect turning on the good genes, the disease-preventing genes
    turning off the disease-promoting genes.

    And so these findings I think are really very powerful
    giving many people new hope and new choices
    And companies like Navigenix and DNA Direct and 23andMe,
    that are giving you your genetic profiles,
    are giving some people a sense of, “Gosh, well what can I do about it?”
    Well, our genes are not our fate, and if we make these changes —
    they’re a predisposition, but if we make bigger changes
    than we might have made otherwise
    we can actually change how our genes are expressed.
    Thank you.
    (Applause)

    Your browser doesn’t support HTML5 video (MP4 or OGG) and doesn’t have Flash Player.
    While you won’t be able to watch the video in your browser, we’ve provided a transcript of the entire video so you won’t be missing out on any important information.
    You can also download the video and view it using your media player of choice:

    Download MP4 version.
    Download OGG version.
    Download FLV version.

    This site works best in Firefox & Chrome.
     

  4. @jeannine i can’t guarantee i’ll have time to look at it, but if you post a link to your file i’ll try and take a look

    EDIT: i think i see your error. you’re confusing transcriptEnabledClass with enableTranscript. double-check their meanings in the docs (posted above). transcriptEnabledClass takes a string as an argument while enableTranscript takes a boolean.

  5. @jeannine yeah, be sure to check your server’s mime settings! if ogv isn’t specified, the file won’t be served correctly. same goes for other file formats, like m4v and flv. glad you got it sorted out.

  6. @john in this case, elementID: “video” is referencing an object element with the ID “video” (not a video element). If the elementID is not supplied, or if it’s invalid (doesn’t match an actual element on your page), the addFallback function will silently fail.

  7. People using EasyCaptions may be interested in trying the free (GPLed) Advene software ( http://www.advene.org/ ) to generate the timestamped data: Advene offers many ways to annotate videos with timestamped data (through video bookmarks, note-taking while watching the video, importing from many others formats such as srt…), and thereafter to generate files based on this timestamp data through templates. So just define your template once, and easily generate your easycaption data.

    You can see an example of generated data (and download the original data file) at http://liris.cnrs.fr/advene/examples/tbl_linked_data_html5/index.html for instance. For fun, note that the textual Summary actually drives the video player to play a new montage of the video.

  8. I am making a little perl script to convert srt to EasyCaptions format available at
    http://silvervalleyreview.com/se_filters/srt2ezcaps.zip
    I tested it with windows and linux output srt files, on linux and windows.

    I have not tested with output from a native mac subtitle editor, nor any that are java based. A prototype of this script has been tested on a mac.

    Anyone who cares to share a sample srt file output from native mac or java based subtitle editors is invited to send it to

    jbdough (at) gmail (dot) com

  9. Hi Philip
    I am kind of stuck making EasyCaptions work with jwplayer version 6.

    How do I get what would be the callback function to work with how jwplayer now handles fallback itself, instead of embedding the swfobject like your examples ?

    I’m trying to do this with a youtube stream. Thanks!

  10. @john I haven’t tried JW Player 6, but after a quick peek at the API, this should work (or point you in the right direction). It’s untested, so please let me know if it works.

    The code shown below was written to replace the JavaScript found in this example: http://lab.pipwerks.com/video/easycaptions/TED/flash-fallback.html

    /* The following code is specific to JW Player 6. */
    
    jwplayer().onReady(function(){
        
        var easy = new EasyCaptions({
            videoElementID: "video-html5",
            transcriptElementID: "transcript",
            transcriptEnabledClass: "enabled"
        });
    
        easy.addFallback({
             
             elementID: "video-swf",
             
             captionHandler: function (video, updateCaption){
                //Nothing needed here; we're using 
                //JW Media Player's "playerReady" function
             },
             
             transcriptHandler: function (video, position){
                            
                //Ensure video is playing before trying to SEEK
                if(jwplayer().getState() !== "PLAYING"){
                    jwplayer().play(true);                
                }
    
                jwplayer().seek(position);
                
             }
             
        });
    
        //update caption whenever the playback position gets updated
        jwplayer().onTime(function (ev){
            easy.updateCaption(parseInt(ev.position, 10));
        });
        
    });
    
  11. thanks Philip – look at what I have when you get the chance –
    I basically defined the var callback the same old way, and then

    
        jwplayer('video').setup({
            file: 'http://www.youtube.com/watch?v=N76ByvA25ag',
    	image: 'shot0001.png',
            width: '420',
            height: '315'
        });
        jwplayer().onReady(callback);
    
    

    I know the onReady is firing because the styling onHover and the clickable notice appears. Now to debug – maybe it has something to do with playerReady being deprecated.

  12. @john your code is not taking into account the jwplayer API changes, as outlined in my code sample above (eg jwplayer().getState() replacing video.getConfig().state). Have you tried using the code I supplied?

  13. It works!!
    Sorry Philip, I blew it pasting

    jwplayer().onTime(function (ev){
    easy.updateCaption(parseInt(ev.position, 10));
    });

    I messed up on that last bracket-paren-semicolon with what was there before.

  14. Hi, just came across this great source and doing some testing. Should enableCaptions: false work with the video plus flash (fallback) version? I can’t get the the captions to not appear with this demo.

    1. @anthony The EasyCaptions code is not configured to handle your use case, but it can be added pretty easily. I don’t have time to play with it this week, but if you submit a feature request on GitHub I will try to get to it when I have time.

  15. Thanks, I appreciate the quick reply! I’ve submitted an Issue on Github instead of Pull Request because I am not familiar in using Github.

Leave a Reply

Your email address will not be published. Required fields are marked *