Post filed in Open Source

Silverlight Video Player updated to display Closed Captioning

Yesterday I completed the first version of Closed Captioning support for the Silverlight 2 Video Player on CodePlex.  It definitely in an alpha phase, but with some polish it should be reliable and more usable.  Here is an image of how the captions are being displayed.


In this first version I made a decision to display closed captioning when the volume is muted, but with the right visualization we should be able to find a nice place for a ToggleButton.  Here is a list of a few other things I did to the project.

  • Updated Xml parsing to support markers with multiple types (Marker, CC, etc)
  • Converted speaker button control to ToggleButton to display a different visualization when the player is muted.
  • Linked volumeSlider to speaker button so slider drops to zero on mute and back to previous volume when mute is turned off.
  • Added a new event from mediaControl that is fired when the volume is changed.  This allows the player to control closed captioning display and not the media controller.
  • Wrote closedCaptioning UserControl that visualizes the scrolling captions.

Asynchronous loading of TimelineMarkers for Silverlight Video Player

As part of the ongoing evolution of our Open Source Silverlight Video Player on CodePlex, I added the ability to asynchronously load a set of TimeLineMarkers from an XML document.  The goal was to allow users a choice between encoding there video with markers or defining those markers in an external xml file.  What I thought would be a pretty trivial task, proved to be an interesting challenge. Here is a snapshot of the Xml format.

marker_1


The first step in implementing this functionality was to download the marker Xml.  In the event that this marker file was quite large in size I decided to make the download asynchronous.  Downloading a file asynchronously required that the path to the file is absolute.  Since I have no real control over my initParams I decided to support both an absolute or relative Uri.  In the event that the path to the Xml document is relative, I make a reasonable assumption that it is located in the same location as the XAP.  As a result ,I can define my Uri with “Host.Source” as the baseUri of the Xml document.  To Download the file, I simply make a WebRequest and define an AsyncCallback to be fired when the file has completed downloading. 

Threading Considerations

In Silverlight 2 Beta 2 there was a significant change in the concurrency model used for asynchronous communications.  In Beta 1 these type of requests returned on the UI thread.  In Beta 2, when you choose to use the BeginGetResponse of the WebRequest you are telling Silverlight to use a worker thread that comes from a thread pool.  As a result, you can NOT update any user interface elements on the UI thread.  Initially I thought I would be able to use “Application.Current.RootVisual.Dispatcher” as a way to execute a delegate on the UIThread.  Unfortunately this throws a Cross Thread exception.  After explored a few options I realized that I could simply call Dispatcher.BeginInvoke(MyMethod) from within my AsyncCallback.  here is what that looked like.

void OpenStreamCompleted(IAsyncResult ar)
{
    HttpWebRequest request = ar.AsyncState as HttpWebRequest
    WebResponse response = request.EndGetResponse(ar)
 
    // Read the response into a Stream object.
    Stream responseStream = response.GetResponseStream()
 
    // Create a new StreamReader instance using the specified Stream object.
    using (StreamReader streamreader = new StreamReader(responseStream))
    {
        XElement document = XElement.Load(streamreader) 
        _externalMarkerData = (from el in document.Elements() select GetMarkerData(el)).ToList()
    }
    Dispatcher.BeginInvoke(ProcessParsedMarkerCollection)
}

As you can see above, once I have the Xml downloaded I parse it from Xml into a collection. For this I thought it would be cool to use Linq for XML.  I am always amazed at how few a lines of code I need to write to parse an Xml document with Linq.  The end product of the parse is a List<> containing a very simple custom object called “MarkerData”.  If I had chosen to move this parsing back to the UIThread I could have just created a collection of TimelineMarkers, but I thought there was no harm in having my own object to expand over time.

To get the latest source visit the project at http://www.codeplex.com/sl2videoplayer

Silverlight 2 Video Player pushed to CodePlex

Yesterday Tim Heuer made a great suggestion that we submit our collaboration on a  Silverlight 2 Video Player as Open Source to CodePlex. You can now find the latest source at http://www.codeplex.com/sl2videoplayer.  As we find any “undocumented features” or add new and exciting ehancments we will push these up to CodePlex.

Google Apps for Domain Email

Google AppsFor those of you in hibernation for the last few years, Google is taking over the world, one free service after another.  This weekend I decided to break ties with Godaddy’s crappy email hosting (5 accounts, 1 GB storage, $20 year) and switch to Google Apps.   My reasons were quite simple, Free is Free and I really wanted IMAP support.  I know that the risk is pretty high with “big brother” now in control, but for IMAP support and >6 GB of storage per account, I think it is worth the risk.

For the most part the switch was painless.  The first step in the process was to create an account and prove that you own the domain.  Proof is achieved by creating a custom CNAME record in your DNS that maps to google.com.  Once the domain is validated you can begin creating user accounts.  In my situation I only had 5 accounts so I created them manually, but for larger organizations they have a “csv” upload utility that imports a large batch.  Once I created all of the accounts, I added any necessary account alias (they call them nicknames) and then switched my hostnames MX records.  Google warns that it could take up to 48 hours, but in about a hour the changes had propagated to my internet provider.

If you choose to make the switch, make sure that you tell of of your users to access there new account via the web first.  Google makes you enter a “captcha” image and agree to a terms screen.  Once this is completed you can enable IMAP support and import you existing email.  Google does NOT provide an import tool, not even from one Gmail account to another.  The approach most take, is enable POP access on your old account, and suck in all of the email using there Mail Fetcher.  Make sure that if you had “labels” you recreate those before importing as well as manually create all of your filters.

FBJS and Ajax to acheive Facebook profile link tracking

In interactive application development we love our ability to track and analyze the behavior of our users. Through tools like Google analytics and Urchin, web sites can monitor and analyze where their users are going, and more importantly how those users are getting there.

As our company delves deeper into creating applications for the Facebook platform, we have realized that analysis of users behavior is extremely important. It is no secret that very few companies have mastered how to create a success Facebook application. Those that have seen moderate success do one thing consistently, they all leverage the social graph, but outside of that there is no clear blue print to success. Using techniques such as user tracking we can begin to tune the way a Facebook applications evolves as a way to change its presentation based on the behaviors of its users.

Integrating analytics into a canvas view it is pretty simple, slap in some Google analytics and you can begin to monitor and tune user traffic. The profile view is much more challenging. In the profile view an applications FBML and images are cached and delivered from a Facebook server. Using traditional Google analytics in the profile is not possible. I hope someday Facebook will show us how many users view and interact with a users profile, but until then we need some way to track a users interaction with our application. Back in October Facebook gave developers the ability to do some restricted Javascript and Ajax. With these two features, we can begin to track what links a user click and how those clicks relate to a users interaction with the canvas view. The following snip of FBJS will post a small JSON array to a external location, which in turn will asynchronously store that data for later analysis.

1
2
3
4
5
6
7
8
9
10
11
12
<script><!--
    var uid = '111111';
    function track(a, pid, method) {
        // ajax query
        var ajax = new Ajax();
        ajax.responseType = Ajax.RAW;
        ajax.requireLogin = false;
        var queryParams = { "pid" : pid, "method" : method, "uid": uid, "source": "profile"};
        ajax.post("http://url.com/tracker.ashx", queryParams);
	return true;
    }
//--></script>

To enable this tracking we must add the following callback attached to that elements onclick event. In our example we are attaching tracking to a link which provides an external vendors affiliate link for a series of consumer products.

One caveat, this approach only works correctly in Firefox if your <a> target is set to blank. If you need to use the same window, the only approach I have found is to use the anchor that is passed into the track method combined with the ajax.ondone callback to set the document.setLocation to the anchors href.

1
2
3
4
5
6
ajax.ondone = function() {
var href = a.getHref();
   if (href.length > 1) {
      document.setLocation(a.getHref());
   }
}
1
<a href="http://www.amazon.com" onclick="track(this,12345,'buy');" class="buy" target="_blank">BUY</a>

On the back end we have a page that is waiting for this specific Ajax post, and will return a 1 for success and a 0 for failure. It is extremely important to keep this tracking as efficient as possible. Facebook Ajax has a <10 second maximum timeout on all requests. In our design, the storing of the data is done using a asynchronous thread that queues up the incoming requests in hoped to avoid Facebook having to wait for us to store the data correctly.

Facebook Developers Garage Presentation

Facebook - Developers Garage - PhoenixOn November 14th @ the Tempe Center for the Arts Terralever hosted Phoenix’s first Facebook Developer’s Garage. The event was a great success.  I especially enjoyed the presentations by Chris Johnson  of Terralever and Dave Morin of Facebook. I encourage everyone to look at Chris slides on marketing a Facebook application. For those of you who might be interested in what I spoke about take a look at my 15 min presentation on the “Anatomy of a Facebook App”  With so little time,  I was unable to jump to deep into the platform, but managed to get through the basic steps to create a your first Facebook Application.

Download: Anatomy of a Facebook Application
Date: November 14, 2007
Location: Tempe Center for the Arts

Facebook - fbml_refreshImgSrc

Maybe its just a temporary thing, but lately the Facebook server has had major issues with caching images.  If you are unaware, all images that are placed within a users profile page get scraped by Facebook and served up from their own internal servers.  If for some unforeseen reason there scraper fails your image gets replaced with an invisible pixel spacer gif.  To force the cache to update you have to manually call the fbml_refreshImgSrc method on their REST server.  The easiest way to accomplish this is to create a dedicated page that uses an infinite session.  Here is the PHP page I created to update the images as necessary.

1
2
3
4
5
6
$myClass = new my_class($api_key, $secret); 
//this sets a user and session 
$myClass ->facebook->set_user($global_user, $global_session); 
if(isset($_GET['url'])) { 
  echo '' . $myClass ->facebook->api_client->fbml_refreshImgSrc($_GET['url']) .''; 
}

Facebook - feed.publishTemplatizedAction

This past week Facebook added a new method on their REST server for sending news and mini feed stories. The method is “feed.publishTemplatizedAction” and the intent of this method is to replace the existing “feed.publishActionOfUser” with a method that is more “templatized”. Here is a sample we created which is a combination of static text and data we gathered from a RSS feed. When a users first installs our application we will publish this news story.

1
2
3
4
5
6
7
8
9
10
11
12
13
actor_id: 555555555
title_template:   {actor} <fb:if-multiple-actors>are<fb:else>is</fb:else>
           </fb:if-multiple-actors> exploring the web’s premier collection of 
            student-produced digital media Made on a Mac. 
title_data: 
body_template: <b>"{title}"</b> Made by <i>{author}</i>  
           - See it at the <a href="http://url/item.php?itemID={id}">
           Apple Student Gallery</a> 
body_data: {"title":"Lost", "author":"Jared A. Jaworski","id":13243} 
body_general: 
image_1: http://url/resize3.php?image=/109.jpg 
image_1_link: http://url/resize4.php?image=/109.jpg 
target_ids:

Sample
As illustrated in the sample, if multiple users publish stories where both the title_template and body_template markup strings are identical, as well as the title_data and body_data arrays, then Facebook may aggregated the stories together. When this occurs, the {actor} token is replaced with the names of all of the users whose actions are being aggregated. Also note, that the only valid HTML tags supported in the template is the <a>, <b> and <i>.

For more information check out Facebooks wiki at Feed.publishTemplatizedAction

Facebook Developer’s Garage - Phoenix

Facebook - Developers Garage - PhoenixOn November 14th @ the Tempe Center for the Arts Terralever will be hosting Phoenix’s inaugural Facebook Developer’s Garage. Anyone who is interested in diving deep into the Facebook Platform is welcome to join. The event is a forum to share ideas with local developers and listen to interesting presentations on the planning, development, implementation, and marketing of Facebook applications.

To see more details and RSVP, follow the link below:
http://www.facebook.com/n/?event.php&eid=5321528989

Facebook <fbml:ref../>

For those of you who have embarked on writing a Facebook app, you may have faced the challenge of how to automatically update your profile fbml without forcing a user to take action. The approach I have found most effective is to use the <fbml:ref> tag. Facebook defines this command as a tag which “fetches and renders FBML from a given ref source – either a ref string “handle” you’ve created using fbml.setRefHandle or a URL that serves FBML. ” When using “fbml.setRefHandle” Facebook stores this FBML on its servers in the form of a hashtable. Using a single push we can update FBML for all users that have our application installed.

For a recent client we were asked to display the first few entries from a RSS feed within a users profile. All Facebook users would see the same entries, until the RSS feed changed. Here is the PHP we used to set a users FBML.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function render_ref_fbml($uid, $rssItems) { 
  global $facebook; 
  try { 
    // Set FBML Cache 
    $refFbml = render_narrow_fbml($id, $rssItems); 
    $refFbml .= render_wide_fbml($id, $rssItems); 
    $facebook->api_client->fbml_setRefHandle('RssFbml',$refFbml); 
    // Set Profile FBML 
    $fbml = render_css_style(); 
    $fbml .= '<fb:ref handle="RssFbml">'; 
    $facebook->api_client->profile_setFBML($fbml, $uid); 
    } catch (Exception $e) { 
      error_log($e->getMessage()); 
    } 
}

In a standalone php file we then created a similar function which updated only the FBML stored under the “RssFbml” handle.

1
2
3
4
5
6
7
8
9
10
11
function render_ref($rssItems) { 
  global $facebook; 
  try { 
    // Set FBML Cache 
    $refFbml = render_narrow_fbml(0, $rssItems); 
    $refFbml .= render_wide_fbml(0, $rssItems); 
    $facebook->api_client->fbml_setRefHandle('RssFbml',$refFbml); 
  } catch (Exception $e) { 
    error_log($e->getMessage()); 
  } 
}

This php file uses an infinite Facebook session and is called every 30 min by a CRON job.

1
30 * * * * php -f /var/www/rss.php