Thursday, October 18, 2012

How to play a video on HTML5 canvas tag


How to play a video on HTML5 canvas tag

Step-by-step tutorial on how to play a video on HTML5 canvas tag



First you need a video file. Actually, two. One video file in ogg format (for Chrome and Firefox) and the other one in mp4 format (for Safari). To get any video in these file formats you can use a website like www.online-convert.com or www.mediaconverter.org.

Once you have the video files, the next step is to create a simple HTML page with one canvas tag, and one video tag. Just like the code below.

 <!DOCTYPE html>  
 <head>  
 <title>Playing YouTube video on HTML5 canvas</title>  
 </head>  
 <body>  
  <video id="video" autoplay="true" loop="true">  
   <source src="./video/BigBuckBunny_640x360.ogv" type="video/ogg" />  
   <source src="./video/BigBuckBunny_640x360.mp4" type="video/mp4" />  
  </video>  
  <canvas id="canvas"></canvas>  
 </body>  
 </html>  

Right now, you should be able to test the video and check if it is playing well on Chrome Safari and Firefox.

Next, we add some CSS and a new hidden div wrapping the video tag.

 <!DOCTYPE html>  
 <head>  
 <title>Playing YouTube video on HTML5 canvas</title>  
 </head>  
 <body>  
  <video id="video" autoplay="true" loop="true">  
   <source src="./video/BigBuckBunny_640x360.ogv" type="video/ogg" />  
   <source src="./video/BigBuckBunny_640x360.mp4" type="video/mp4" />  
  </video>  
  <canvas id="canvas"></canvas>  
 </body>  
 </html>  

Since we will be rendering the video on canvas, we need to make the video hidden. But we will be using the audio from the video tag.

Now the only missing piece is the javascript that renders the video on canvas. To do this we need to grab a image from the video and then paint this image on canvas. If we repeat this process every X milliseconds... TA-DA! We have the video playing on canvas.

Here is the full source code:

 <!DOCTYPE html>  
 <head>  
 <title>Playing YouTube video on HTML5 canvas</title>  
 <meta name="viewport" content="user-scalable=no, initial-scale=1.0, maximum-scale=1.0, width=device-width" />  
 <style type="text/css">  
  html, body {  
   width: 100%;  
   height: 100%;  
   padding: 0px;  
   margin: 0px;  
  }  
  #canvas {  
   padding: 0px;  
   margin: 0px;  
   top:0;  
   left:0;  
   z-index: 30;  
   position: absolute;  
   width: 100%;  
   height: 100%;  
  }  
 </style>  
 </head>  
 <body>  
  <div style="display: none;">  
   <video id="video" autoplay="true" loop="true">  
    <source src="./video/BigBuckBunny_640x360.ogv" type="video/ogg" />  
    <source src="./video/BigBuckBunny_640x360.mp4" type="video/mp4" />  
   </video>  
  </div>  
  <canvas id="canvas"></canvas>  
  <script>  
  document.addEventListener('DOMContentLoaded', function(){  
   var v = document.getElementById('video');  
   var canvas = document.getElementById('canvas');  
   var context = canvas.getContext('2d');  
   //var cw = Math.floor(canvas.clientWidth / 100);  
   //var ch = Math.floor(canvas.clientHeight / 100);  
   var cw = Math.floor(canvas.clientWidth);  
   var ch = Math.floor(canvas.clientHeight);  
   canvas.width = cw;  
   canvas.height = ch;  
   v.addEventListener('play', function(){  
    draw(this,context,cw,ch);  
   },false);  
  },false);  
  function draw(v,c,w,h) {  
   if(v.paused || v.ended) return false;  
   c.drawImage(v,0,0,w,h);  
   setTimeout(draw,20,v,c,w,h);  
  }  
  </script>  
 </body>  
 </html>  


If you want to play a video from YouTube, check this tutorial: Playing YouTube videos on HTML5 canvas with Javascript.

If you want to download a video from YouTube, check this one: Download YouTube video files with Javascript

Next, I'll show you how to make a simple HTML5 game puzzle. Stay tuned!


Wednesday, October 17, 2012

Download YouTube video files with Javascript

UPDATED! 

Download YouTube video files with Javascript

How to download YouTube videos files with Javascript only! Is it possible?

YES! It is possible! And without relying in any kind of server side scripts!






Libraries needed to do this:
  • jQuery

Step-by-step: Downloading YouTube videos with Javascript.

  • Part 1: Get the YouTube video URL from an input and validate the URL using regular expression. 
  var url = $('#url').val();  
  var regExp = /.*(?:youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=)([^#\&\?]*).*/;  
  var match = url.match(regExp);  
  if(match){  
   $('#download-btn').fadeOut('fast');  
   $('#loading').fadeIn('slow');  
   setTimeout("getVideo('"+match[1]+"')",2000);  
  }  
  else{  
   alert('Invalid URL!');  
   $('#url').val("");  
   $('#url').focus();  
  }  

  • Part 2: Retrive raw video information from YouTube
    • Make an Ajax call to http://www.youtube.com/get_video_info?video_id=XXX

       $.ajax({  
        url: "http://www.youtube.com/get_video_info?video_id="+id,  
        dataType: "text"  
       }).done(function(data) {
        ...  


  • Part 3: Prepare information to be parsed. We need to do 3 things:
    • a) Replace all occurrences of the character "\" to "%20"

       data = (data+'').replace(/\+/g, '%20');  
      

    • b) Replace all occurrences of the string "url%3D" to "\n\r<break>"

       data = data.replace(/url%3D/g, '\n\r\n\r<break>');  
      

    • c) Replace all occurrences of the string "sig%3D" to "signature%3D"

       data = data.replace(/sig%3D/g, 'signature%3D');  
      


  • Part 4: Grab all files URLs from this YouTube video
  var urls = data.split('<break>');   
  for(var u = 0; u < urls.length; u++){   
  var result = {};   
  ...   
  }   

  • Part 5: Prepare each URL to be parsed
    • a) Decode the URL

       decodeURIComponent((urls[u]+'')  
      
    • b) Replace all occurrences of the character "\" to "%20"

       url = url.replace(/\+/g, '%20');  
      
    • c) Unescape the result twice

       url = unescape(unescape(url));  
      
    • d) Replace all occurrences of the string '="' to '%3D%22'

       url = url.replace(/="/g, '%3D%22');  
      
    • e) Replace all occurrences of the character '"' to "%22"

       url = url.replace(/"/g, '%22');  


  • Part 6: Return a list for the videos URLs present in the YouTube results.
    • a) Parse all variables present in each URL

       var vars = [], hash;  
       var hashes = d.slice(d.indexOf('?') + 1).split('&');  
       for(var i = 0; i < hashes.length; i++){  
        hash = hashes[i].split('=');  
        vars.push(hash[0]);  
        vars[hash[0]] = unescape(hash[1]);  
       }  
      
    • b) Grab the video type and codecs from the URL parameters

       if(vars['type']!=undefined){  
        result.type = vars['type'];  
        if(vars['type'].indexOf("codecs")>0){  
         var cs = vars['type'].split(';+codecs="');  
         result.type = cs[0];  
         result.codecs = cs[1].replace('"','').replace('+',' ');  
        }  
       }  
      
    • c) Grab the video quality from the URL parameters

       //quality  
       if(vars['quality']!=undefined){  
        result.quality = vars['quality'];  
        if(vars['quality'].indexOf(",")>0){  
         var cs = vars['quality'].split(',');  
         result.quality = cs[0];  
        }  
       }  

That is it. Now you have all information needed in the result object. Here is the full source-code:

 /* Author: Emerson Estrella */  
 $('#again').click(function() {  
  $('#hero2').fadeOut('fast');$('#hero1').fadeIn('slow');  
 });  
 $('#form').submit(function() {  
  var url = $('#url').val();  
  var regExp = /.*(?:youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=)([^#\&\?]*).*/;  
  var match = url.match(regExp);  
  if(match){  
   $('#download-btn').fadeOut('fast');  
   $('#loading').fadeIn('slow');  
   setTimeout("getVideo('"+match[1]+"')",2000);  
  }  
  else{  
   alert('Invalid URL!');  
   $('#url').val("");  
   $('#url').focus();  
  }  
  return false;  
 });  
 function getVideo(youtube_video_id){  
  var id = "b4VsluhWVh8";  
  id = youtube_video_id;  
  $.ajax({  
   url: "http://www.youtube.com/get_video_info?video_id="+id,  
   dataType: "text"  
  }).done(function(data) {  
   var results = [];  
   var r = 0;  
   data = (data+'').replace(/\+/g, '%20');  
   data = data.replace(/url%3D/g, '\n\r\n\r<break>');  
   data = data.replace(/sig%3D/g, 'signature%3D');  
   var urls = data.split('<break>');  
   for(var u = 0; u < urls.length; u++){  
    var result = {};  
    var d = unescape(unescape(decodeURIComponent((urls[u]+'').replace(/\+/g, '%20'))));  
    d = d.replace(/="/g, '%3D%22');  
    d = d.replace(/"/g, '%22');  
    var url = d;  
    //console.log(d);  
    //console.log(d.length);  
    if(d.length > 1500){  
     aux = d.split('&has_cc');  
     d = aux[0];  
    }  
    var vars = [], hash;  
    var hashes = d.slice(d.indexOf('?') + 1).split('&');  
    for(var i = 0; i < hashes.length; i++){  
     hash = hashes[i].split('=');  
     vars.push(hash[0]);  
     vars[hash[0]] = unescape(hash[1]);  
    }  
    if(vars['type']!=undefined){  
     result.type = vars['type'];  
     if(vars['type'].indexOf("codecs")>0){  
      var cs = vars['type'].split(';+codecs="');  
      result.type = cs[0];  
      result.codecs = cs[1].replace('"','').replace('+',' ');  
     }  
    }  
    //quality  
    if(vars['quality']!=undefined){  
     result.quality = vars['quality'];  
     if(vars['quality'].indexOf(",")>0){  
      var cs = vars['quality'].split(',');  
      result.quality = cs[0];  
     }  
    }  
    if(result.type && result.quality){  
     result.url = url;  
     results[r] = result;  
     r++;  
    }  
   }  
   //console.log(results);  
   //print results  
   var html = '';  
   html += '<h4 class="alert-heading" style="margin-top: 25px;">All video files found for your request</h4>';  
   html += '<a id="again" style="margin-top: 25px;" class="btn btn-small btn-danger" href="#main" onclick="$(\'#hero2\').fadeOut(\'fast\');$(\'#hero1\').fadeIn(\'slow\');"><i class="icon-repeat icon-white"></i> Make Another Request</a>';  
   html += '<table class="table table-striped musica" style="background: rgba(255,255,255,0.7); margin-top:25px;"><thead><tr><th>Quality</th><th>Format</th><th>Codecs</th><th style="text-align: right;"></th></tr></thead><tbody>';  
   $.each(results, function(index, value) {  
    html += '\n\r<tr>';  
    html += '<td>'+value.quality+'</td>';  
    html += '<td>'+value.type+'</td>';  
    if(value.codecs!=undefined)  
     html += '<td>'+value.codecs+'</td>';  
    else  
     html += '<td>N/A</td>';  
    html += '<td><a class="btn btn-success pull-left" href="'+value.url+'" style="margin-right: 15px;"><i class="icon-download-alt icon-white"></i> Download this video format file</a></td>';  
    html += '</tr>\n\r';  
   });  
   html += '</tbody></table><a style="margin-top: 10px; margin-bottom: 25px;" class="btn btn-small btn-danger" href="#main" onclick="$(\'#hero2\').fadeOut(\'fast\');$(\'#hero1\').fadeIn(\'slow\');"><i class="icon-repeat icon-white"></i> Make Another Request</a>';  
   $('#vid').html(html);  
   $('#vid').fadeIn('slow');  
   $('#loading').hide();  
   $('#hero1').hide();  
   $('#hero2').fadeIn('slow');  
   $('#download-btn').show();  
  });  
 }  





In the next post I'll explain how to Download YouTube video files from a server side script like PHP. Stay tuned!


Playing YouTube videos on HTML5 canvas with Javascript

Playing YouTube videos on HTML5 canvas with Javascript

How to play YouTube videos on HTML5 canvas tag

This blog post is coming soon. Stay tuned!

Saturday, October 13, 2012

Step-by-step creating a HTML5 image puzzle game

Step-by-step creating a HTML5 image puzzle game

How to create a HTML5 image puzzle game with images, canvas, audio and fullscreen support.

Tutorial coming soon 

Adsense - Responsive Ads using a Javascript interface

Adsense - Responsive Ads with a Javascript interface

How to make responsive ads using Javascript

So, the idea is check the user screen size and then based on that value show up the ads.

In my case I have the following set:
  • Desktop
    • Leaderboard (728x90)

  • Portrait tablet to landscape and desktop
    • Banner (468x60)

  • Landscape phone to portrait tablet
    • Banner (468x60)

  • Landscape phones and down
    • Half Banner (234x60)


See the source code example:

 <script type="text/javascript">  
       google_ad_client = "ca-pub-XXXXXX";  
       google_ad_slot = "";  
       if(window.innerWidth >= 800) {  
        //visible-desktop  
        //Medium Rectangle  
        google_ad_slot = "xxxxxxx";  
        google_ad_width = 300;  
        google_ad_height = 250;  
       }  
       else if((window.innerWidth > 480)&&(window.innerWidth < 800)) {  
        //visible-tablet  
        //Square  
        google_ad_slot = "xxxxxxx";  
        google_ad_width = 250;  
        google_ad_height = 250;  
       }  
       else if(window.innerWidth <= 480) {  
        //visible-phone  
        //Large Rectangle  
        google_ad_slot = "xxxxxxx";  
        google_ad_width = 336;  
        google_ad_height = 280;  
       }  
       if(google_ad_slot != "")  
        document.write('1: <scr'+'ipt type=\"text/javascript\" src=\"http://pagead2.googlesyndication.com/pagead/show_ads.js\"></scri'+'pt>');  
       </script>  

Are you using Twitter Bootstrap? If so, you can make the same thing quite easier. Take a look!

Friday, October 12, 2012

Adsense - Responsive Ads with CSS @media queries from Bootstrap responsive utility classes

Adsense - Responsive Ads with Bootstrap and CSS @media queries


Twitter Bootstrap is built with Responsive utility classes that help us to implement different sizes of ads   for different devices.

ClassPhones767px and belowTablets979px to 768pxDesktopsDefault
.visible-phoneVisible
.visible-tabletVisible
.visible-desktopVisible
.hidden-phoneVisibleVisible
.hidden-tabletVisibleVisible
.hidden-desktopVisibleVisible
So, the idea is to use these classes to show the right ads. In my case I have the following set:

  • Desktop
    • Leaderboard (728x90)


  • Portrait tablet to landscape and desktop
    • Banner (468x60)

  • Landscape phone to portrait tablet
    • Banner (468x60)

  • Landscape phones and down
    • Half Banner (234x60)


It is really simple to achieve this using the Bootstrap built-in responsive utility classes. See the example bellow:


 <div class="visible-desktop">  
        <script type="text/javascript">  
             google_ad_client = "ca-pub-XXXXXX";  
             google_ad_slot = "XXXXXXX";  
             google_ad_width = 728;  
             google_ad_height = 90;  
        </script>  
        <script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"></script>   
 </div>  

 <div class="visible-tablet">  
        <script type="text/javascript">  
             google_ad_client = "ca-pub-XXXXXX";  
             google_ad_slot = "XXXXXXX";  
             google_ad_width = 468;  
             google_ad_height = 60;  
        </script>  
        <script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"></script>
 </div>  

 <div class="visible-phone">  
        <script type="text/javascript">  
             google_ad_client = "ca-pub-XXXXXX";  
             google_ad_slot = "XXXXXXX";  
             google_ad_width = 234;  
             google_ad_height = 60;  
        </script>  
        <script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"></script>
 </div>  

Easy uh?!

Here is a example showing how we can make the same thing using Javascript only.



Sunday, September 9, 2012

iOS 5.1.1 Development on Xcode 4.3 - Testing on real device without an Apple Provisioning Profile

iOS 5.1.1 Development on Xcode 4.3

Testing on real device without an Apple Provisioning Profile

After a two long days, today I was able to build and run an iOS 5.1.1 from Xcode straight to my device.

To do this you will need:
  • Jailbroken iPhone, iPad or iPod
  • Appsync installed on device
  • Xcode 4.3+

Step 1: Create a new self-signed code-signing certificate using Keychain Access
  • Open the Keychain Access application
  • Go to Menu "Keychain Access" -> "Certificate Assistant" -> "Create a Certificate"
  • Choose an unique name, easier to remember. You will need that name latter on.
  • Choose "Self Signed Root" for "Identity Type"
  • Choose "Code Signing" for "Certificate Type"
  • Check "Let me override defaults"
  • And then click "Create"
  • Click next to all following questions without changing anything

Step 2: Edit Xcode "Info.plist" file
  • Open the file: /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Info.plist
  • Replace the three occurrences of "XCiPhoneOSCodeSignContext" by "XCCodeSignContext"
  • Save the file and restart Xcode

Step 3: Install Appsync
  • Intall "Appsync" application from Cydia
  • Intall "SSH" application from Cydia
  • Log into your device via SSH and create a blank file called "tdmtanf" in "/var/mobile"

Step 4: Xcode Project Setup
  • Open your project on Xcode
  • Go to "Project" -> "Build Settings" -> "Code Signing" -> "Code Signing Identity" -> Choose the identity related to the certificate that you created on step 1 on settings for debug and release.
  • Make exactly the same thing on "Targets" -> "Build Settings" -> "Code Signing" -> "Code Signing Identity"

Now you should be able to build and run your project from Xcode to your device.

Sunday, July 1, 2012

Touch != mouse

Touch != mouse

  1. No hover state
  2. Multiple points
  3. Less precise input
  4. Fingers aren't (x, y), but also have shape
  5. Future: Pressure? Haptic feedback? Hover?

Friday, June 22, 2012

Conventional Debugging vs. TDD


Conventional Debugging vs. TDD


Conventional 

  • Write 10s of lines, run, hit bug: break out debugger
  • Insert printfʼs to print variables while running repeatedly
  • Stop in debugger, tweak/set variables to control code path
  • Dammit, I thought for sure I fixed it, now have to do this all again

TDD

  • Write a few lines, with test first; know immediately if broken
  • Test short pieces of code using expectations
  • Use mocks and stubs to control code path
  • Re-run test automatically

Friday, June 15, 2012

Testing Software

Testing Software

Before

  • Developers finish code, some ad-hoc testing 
  • “toss over the wall to Quality Assurance [QA]”
  • QA people manually poke at software

Today

  • Testing is part of every Agile iteration
  • Developers responsible for testing own code
  • Testing tools & processes highly automated; 
  • QA/testing group improves testability & tools

Software quality is the result of a god process, rather than the responsibility of one specific group.

 


Monday, May 7, 2012

HTML5 - Javascript Performance Tests - jsperf.com

jsperf is one of the best things I have seen this year. It is a powerful test engine for Javascript.

Every test created is public, and listed there. So anybody can test it, in any platform or browser and all test results are keept. So as more people runs each test, more analysis data we have.

To see how it works, and also to answer one question regarding the performance of drawing text with shadow on canvas, I have setup a test on jsperf.

http://jsperf.com/requestanimationframe-canvas-drawing-text-shadows

As I only tested on Chrome and Firefox what I can say for now is:

On Chrome 18.0 the performance dorps by almost half when drawing texts with shadows.

On Firefox 12.0 is the same thing. Doesn't matter it keep the same performance, but comparing the results Chrome is almost 50% faster than Firefox when drawing texts without shadows.

Remember that it is only a test and I have only test it on my computer.

Saturday, April 28, 2012

HTML5 AppCache API - How to prevent browser caching unsupported resources in manifest file

The method describe below doesn't work in all browsers.  
For now I'm using two different html files. But the best solution is on server side, making a rewrite rule for redirecting the user to the correct manifest file.


I'm writing a web application that uses the AppCache API for offline browsing. But I'm also using the Audio API to play back-ground music and a few audio effects.

For audio support in different browsers I'm delivering each sound/music in two different file formats: OGG and MP3.

The problem is regarding the cache manifest file. If we add all audio files (MP3 and OGG) in the cache manifest file, all browsers will cache all files. Including the unsupported ones. So, we end up with a huge storage requirement. Which is really bad if you are on a 3G connection for example.

So, to prevent browser caching unsupported resources, the best approach I've found was to split the manifest file. This way we can tell browsers that sopport OGG files to cache only OGG files, and do tha same for other formats like MP3.

Ok then. I have two manifest cache files, one with all OGG files listed (ogg.appcache) and the other one with the MP3 files (mp3.appcache).

Now what!? How do I swap from one to other?

Easy. First we should consider the browser supports OGG. If so, we can add the manifest attribute into the html tag with the right manifest file, just like that:


<html manifest="ogg.appcache">

Now, we have to check if the browser supports MP3. If it does, we change the value of the attribute "manifest" from the html tag. We can do that with this javascript:

var audio = document.createElement('audio');
if(audio.canPlayType('audio/mpeg;'))
  document.documentElement.setAttribute("manifest"document.documentElement.getAttribute("manifest").replace('ogg','mp3'));



Monday, April 23, 2012

HTML5 Polyfills - Fullscreen & RequestAnimationFrame APIs

So far, those are the best polyfills for using Fullscreen and RequestAnimationFrame features from HTML5 on today's browsers.


RequestAnimationFrame API

Optimize concurrent animations together into a single reflow and repaint cycle, leading to higher fidelity animation, and using less CPU, GPU, memory, and battery.


http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating

Fullscreen API

Provides an easy way for web content to be presented using the user's entire screen.


https://github.com/sindresorhus/screenfull.js