(X) Hide this
    • Login
    • Join
      • Generate New Image
        By clicking 'Register' you accept the terms of use .

Silverlight and Flash Interoperability using HTML Bridge and ExternalInterface API

(5 votes)
Jonas Follesø
>
Jonas Follesø
Joined Jul 19, 2008
Articles:   1
Comments:   0
More Articles
6 comments   /   posted on Jul 24, 2008
Tags:   html-bridge , interop , webcam , flash , javascript , jonas-folleso
Categories:   General

Note: This article is submitted by Jonas Follesø for Silverlight Contest: Write and Win.Thanks a lot, Jonas! Hello All, Please drop a comment if you like it.

This article is compatible with the latest version of Silverlight.


Introduction

Silverlight and Flash have many similarities. Both are browser plug-ins that enables web developers to build richer internet applications. You can choose to have Silverlight or Flash occupy the entire screen and build all off the application, or you can have Silverlight or Flash embedded as small islands on your HTML page.

When you decide to include Silverlight or Flash on your page there are many scenarios where you may want to enable the application to interact with the rest of your page. You may want to read data from the HTML page, or update certain HTML elements at runtime. Perhaps you are gradually enhancing an existing web application and need to leverage existing JavaScript code running on the page. In these scenarios you need to leverage the browser integration model supported by Silverlight or Flash to communicate between your application and the hosting HTML page.

In this tutorial I’ll walk you through the basics of browser integration, and how you can make Silverlight and Flash talk to each. The tutorial starts off with some basic examples of browser integration in Flash and Silverlight, and move on to show how to enable webcam support in Silverlight using Flash.

Download source code

Flash ExternalInterface API

Flash has been around for a long time and there have been different techniques to communicate between Flash and the. The “Flash JavaScript Integration Kit” is one example of such a framework. In Flash 8 Adobe included an official API to enable integration between your Flash application and the browser called the “ExternalInterface API”. The API consists of two main functions: the “addCallback” function to expose an ActionScript function to the web page, and the “call” function to invoke JavaScript functions on the web page.

The following code shows how to create a simple ActionScript function and expose it using the ExternalInterface API:

 ExternalInterface.addCallback("saySomething", saySomething);
  
 function saySomething():String
 {
     return "Hi, I'm Flash!";
 }

To call the function from JavaScript you need to get a reference to the Flash player on the HTML page, and simply call the “saySomething” function:

 function callFlash()
 {
     // Get a reference to the player
     var player = null;
     
     if (document.all)        
 player = document.all("flashPlayer");            
     else
          player = document.getElementById("flashPlayerEmbedded");
      
      alert(player.saySomething());                
  }

If you want to call a JavaScript function from ActionScript you simply use the “call” function on the ExternalInterface API:

 function clicked(eventArgs:Object):void 
 {
     ExternalInterface.call("saySomething", "What's up? I'm calling from Flash!");
 }

Off course you also need to have the “saySomething” JavaScript function defined on your page:

 function saySomething(something)
 {
     alert(something);
 }

As you can see the ExternalInterface API makes it really easy to communicate between Flash and the browser. This first example use simple strings as parameters, but you can pass any ActionScript type across the ExternalInterace API. That being said, in many cases passing strings is all you need as you can format your data using JSON or XML.

Silverlight HTML Bridge

The browser integration layer in Silverlight is often called the “HTML Bridge”, and enables you to interact with both HTML elements as well as JavaScript code running in the browser. This opens up for several interesting integration scenarios, some not even using Silverlight for the user interface at all.

At the core of the HTML bridge is the “ScriptObject”, which objects such as HtmlObject, HtmlPage, and HtmlElement derive from. To invoke the “saySomething” function from our previous example you simply write:

 HtmlPage.Window.Invoke("saySomething", "Can you see the light? The Silverlight!");

The HTML bridge contains a lot of functionallity in addition to basic function invocation. It contains functionalliy to grab a HTML DOM Element and change it’s properties and style attributes. You can expose .NET types to the JavaScript runtime and then create new instances of the type from JavaScript. You can also expose instances of .NET objects to the JavaScript and directly interact with the object.

For a good introduction to interaction between HTML and managed code I recommend checking out the Silverlight quick starts at http://www.silverlight.net/QuickStarts/Dom/. The focus on this article will be on Silverlight to Flash integration, and will not go in-depth on the Silverlight HTML Bridge.

Calling Flash from Silverlight

The Flash ExternalInterface API and the Silverlight HTML gives us the building blocks we need to start passing data between a Silverlight and Flash application running on the same page. For this example we’re going to create a simple user interface to edit a person in both Flash and Silverlight. The interface looks something like this:

In the example I want to show how to call Flash from Silverlight, and Silverlight from Flash. We’ll start by covering how to read a Person object from Flash into Silverlight when the user clicks the “Load from Flash” button.

The first thing we need to is create an ActionScript function to get a Person object from the Flash application. Both JavaScript and ActionScript are following based on the ECMAScript specification, and support object literals. This is a straight forward syntax to create new objects on the fly. For more information check out object literals in ECMAScript check out http://en.wikipedia.org/wiki/Object_literal.

 ExternalInterface.addCallback("getPerson", getPerson);
  
 function getPerson():Object
 {
     return { name: txtName.text, email: txtEmail.text, url: txtUrl.text };
 }

The object returned contains three properties, name, email and url. To get the object from Flash into Silverlight we use a little JavaScript helper function:

 function loadFromFlash()
 {
     var flash = getFlash();
     if(flash)
     {
         return flash.getPerson();                                
     }
 }

The final piece is the C# code to handle the button click and call the JavaScript helper function. The code calls the JavaScript function and gets a generic ScriptObject back. This object contains a ConvertTo-method that tries to convert a script object to a .NET type. To hold the person data we’ve created a simple .NET person object. The property names have to match in order for the conversion to successes:

 public class Person
 {
     public string name { get; set; }
     public string url { get; set; }        
     public string email { get; set; }
 }
 private void btnLoad_Click(object sender, RoutedEventArgs e)
 {
 ScriptObject scriptObject = 
               (ScriptObject)HtmlPage.Window.Invoke("loadFromFlash");
   
      var person = scriptObject.ConvertTo();           
      txtName.Text = person.name;
      txtEmail.Text = person.email;
      txtUrl.Text = person.url;
  }

When we run the application we can enter data in the Flash application, and hit the “Load from Flash” button to update the Silverlight application. The next step is the “Save to Flash” functionality. When the user clicks the “Save to Flash” button we want to push the .NET object from Silverlight to Flash. The HTML bridge supports passing .NET objects from Silverlight to JavaScript by marking an object as scriptable:

 [ScriptableType]
 public class Person
 {
     [ScriptableMember]
     public string name { get; set; }
  
     [ScriptableMember]
     public string url { get; set; }        
  
      [ScriptableMember]
      public string email { get; set; }
  }

Once the type is marked as scriptable we can pass it to any JavaScript function invoked using the HTML bridge. In theory we should be able to pass this type along to Flash, but for some reason the Flash plug-in is throwing an exception when you try passing it a scriptable type. There are several possible workarounds. One option is to pass the object to JavaScript, and then write JavaScript code to create a new object before passing on to Flash. Another option is to use the Silverlight JSON serializer and pass the object as a JSON-string to the JavaScript function, and then evaluate the string before passing it as a JavaScript object to Flash. I’ve included sample code for both solutions.

The first option is to pass the object directly to the JavaScript function, and then create a JavaScript object on the fly before passing it to Flash:

 private void btnSave_Click(object sender, RoutedEventArgs e)
 {
     var person = new Person();
     person.name = txtName.Text;
     person.email = txtEmail.Text;
     person.url = txtUrl.Text;
     HtmlPage.Window.Invoke("saveToFlash", person);
 }

The “saveToFlash” function creates a new JavaScript object before passing it along to Flash. The optimal solution would be to pass the object straight to Flash:

 function saveToFlash(person)
 {
     var flash = getFlash();
     if(flash)
     {          
         var jsPerson = { name: person.name, 
                          email: person.email, 
                          url: person.url };
         flash.setPerson(jsPerson);
      }
  }

The ActionScript function to update the Flash UI is really straight forward:

 ExternalInterface.addCallback("setPerson", setPerson);
  
 function setPerson(person:Object):void
 {
     txtName.text = person.name;
     txtEmail.text = person.email;
     txtUrl.text = person.url;
 }

The second approach for passing our Person object from Silverlight to Flash is to serialize it to JSON before passing it to the browser. JSON stands for “JavaScript Object Notation”, and is a way to represent a full object graph as a string. Basically it’s just a string containing object literals, which can be evaluated by the JavaScript engine to build an object three. Silverlight has built-in support for JSON serialization, so implementing this is fairly straight forward. We’re going to use the DataContractJsonSerializer class, which is located in the System.ServiceModel.Web assembly under the System.Runtime.Serialization.Json namespace. We also need to reference the System.Runtime.Serialization assembly to get the DataContract and DataMember attributes we need to tag our Person object with.

After adding the DataContract and DataMember attributes our Person object now looks like this:

 [DataContract]
 public class Person
 {
     [DataMember]
     public string name { get; set; }
  
     [DataMember]
     public string url { get; set; }                
  
      [DataMember]
      public string email { get; set; }
  }

Using C# extension methods you attach a “ToJson”-method to all objects for an easy way to serialize any object. An extension method is basically a public static method in a static class, where the first parameter has the “this” keyword:

 public static class Extensions
 {
     public static string ToJson(this object obj)
     {
         using(MemoryStream ms = new MemoryStream())  
         {
 DataContractJsonSerializer serializer = new 
 DataContractJsonSerializer(obj.GetType());
  
               serializer.WriteObject(ms, obj);
               ms.Position = 0;                
         using(StreamReader reader = new StreamReader(ms)) 
               {
                  return reader.ReadToEnd();
               }
          }
      }
  }

So in order to pass the JSON –serialized Person object to our JavaScript function we simply need to call the “ToJson” method on the person object:

 private void btnSave_Click(object sender, RoutedEventArgs e)
 {
     var person = new Person();
     person.name = txtName.Text;
     person.email = txtEmail.Text;
     person.url = txtUrl.Text;
     HtmlPage.Window.Invoke("saveToFlash", person.ToJson());
 }

We only need a minor change in the JavaScript function, where we evaluate the JSON string to build a JavaScript object we can pass to Flash:

 function saveToFlash(person)
 {
     var flash = getFlash();
     if(flash)
     {         
         var jsPerson = eval('(' + person + ')');                
         flash.setPerson(jsPerson);
     }
 }

Calling Silverlight from Flash

So far we’ve covered how to call ActionScript functions from Flash. Now we want to do the opposite, call Silverlight functions from ActionScript. The first thing we want to implement is the “Load from Silverlight”-button. When the user clicks the button in Flash we want to call Silverlight to get our Person object. To do that we add a new method to your Silverlight application, as well as mark the user control as a scriptable type, and the method as a scriptable member:

 [ScriptableType]
 public partial class Page : UserControl
 {
     [ScriptableMember]
     public string GetPerson()
     {
         var person = new Person();
         person.name = txtName.Text;
         person.email = txtEmail.Text;
          person.url = txtUrl.Text;
          return person.ToJson();
      }
      // Additonal code removed from example...
  }

We also need to expose an instance of the user control to the browser so that we can call the GetPerson-method from JavaScript:

 public Page()
 {
     InitializeComponent();
     HtmlPage.RegisterScriptableObject("Page", this);
 }

The next thing we need to do is to create a JavaScript function to be called from Flash to get the Person object from Silverlight:

 function loadFromSilverlight()
 {                    
     var silverlight = getSilverlight();
     if(silverlight)
     {
         var jsPerson = eval('(' + silverlight.content.Page.GetPerson() + ')');
         return jsPerson;
     }
 }

To call the Silverlight method we get an instance of the Silverlight control and then access its content property. For each object we’ve registered as scriptable we can access it based on its key. The Page was registered with the key “Page”, so to syntax to call the GetPerson-method looks like this:

silverlight.content.Page.GetPerson()

The final step is to add the ActionScript code to handle the Flash button click event and call the JavaScript function:

 function loadClicked(eventArgs:Object):void 
 {
     var person = ExternalInterface.call("loadFromSilverlight");
     txtName.text = person.name;
     txtEmail.text = person.email;
     txtUrl.text = person.url;
 }
 this.btnLoad.addEventListener("click", loadClicked);

Now that we can load from Silverlight to Flash we want to be able to save. The steps involved should be familiar by now. We start by adding a scriptable method on the Silverlight page:

 [ScriptableMember]
 public void SetPerson(ScriptObject scriptPerson)
 {
     var person = scriptPerson.ConvertTo();
     txtName.Text = person.name;
     txtEmail.Text = person.email;
     txtUrl.Text = person.url;
 }

The only important thing is the parameter type. We accept a ScriptObject which we convert to a Person object, before updating the UI. We also need to add some JavaScript to call from Flash:

 function saveToSilverlight(person)
 {
     var silverlight = getSilverlight();
     if(silverlight)
     {
         silverlight.content.Page.SetPerson(person);
     }
 }

In this example there is no type conversion, we’re passing the ActionScript person object straight to the SetPerson-method. We also need to add a click event handler for the Flash button:

 function saveClicked(eventArgs:Object):void
 {
     ExternalInterface.call("saveToSilverlight",
     { 
         name: txtName.text, 
         email: txtEmail.text, 
         url: txtUrl.text 
     });    
 }

Again we use object literals to create an on-the-fly person object that we pass to the JavaScript function.

Silverlight web cam support using Flash interoperability

Now that we’ve seen how we can integrate Silverlight and Flash using the HTML Bridge and ExternalInterface API it’s time to do something (somewhat) useful. We’re going to enable some basic webcam support in Silverlight using Flash interoperability.

Even though Flash and Silverlight have much of the same functionality, there are certain things you cannot do on both platforms. Local access of webcam and microphone is one example of something that is only possible in Flash. The way webcams work in Flash is through a camera–object. You attach the camera object to a video-object do display the video stream. The ActionScript code to create a camera and video looks like this:

 var cam:Camera = Camera.getCamera();
 cam.setMode(320, 240, 12);
 var video:Video = new Video(320, 240);
 video.attachCamera(cam);
 this.addChild(video);

When you run your Flash application you’ll see a dialog asking if you want to give permission to access webcam and microphone. If you give the permission the video from your webcam should show on your screen.

With the video showing on screen, the next step is to add expose some ActionScript functions to take a snapshot of the video, and send the snapshot back to Silverlight. Flash supports drawing a frame from the video-object into a BitmapData-object. Using the open source as3corelib ActionScript extensions we can use a PNG-encoder to encode the BitmapData into a byte array. This byte array can be encoded as a Base64 string and passed to the browser using the ExternalInterface API:

 import com.adobe.images.PNGEncoder;
 import com.dynamicflash.util.Base64;
  
 flash.system.Security.allowDomain("*");
 ExternalInterface.addCallback("takeSnapshot", takeSnapshot);
  
 function takeSnapshot():String
 {
     var bmd:BitmapData = new BitmapData(video.width, video.height, 
                                          false, 0x00CCCCCC);
      bmd.draw(video, new Matrix());
   
      var pngBytes:ByteArray = PNGEncoder.encode(bmd);
      return Base64.encodeByteArray(pngBytes);
  }

Using the code above we have a way to call our Flash application to get a Base64 encoded PNG image as a string. This string can be passed to Silverlight, which can decode the Base64 data into a byte array, and load the PNG image into a BitmapImage that can be used as the source of a Silverlight Image control. The JavaScript function to call Flash and pass the string back is really simple:

 function takeSnapshot()
 {
     var flash = getFlash();
     if(flash)
     {
         return flash.takeSnapshot();
     }
 }

The click event handler for the “take snapshot”-button calls the JavaScript function to get the Base64 encoded PNG image, decode it and set it as the source of a Silverlight image control:

 void btnTakeSnapshot_Click(object sender, RoutedEventArgs e)
 {
     string base64Image = (string)HtmlPage.Window.Invoke("takeSnapshot");
     if (base64Image != string.Empty)
     {
         byte[] imageData = Convert.FromBase64String(base64Image);
         MemoryStream ms = new MemoryStream(imageData);
         BitmapImage bitmapImage = new BitmapImage();
         bitmapImage.SetSource(ms);
          ms.Dispose();
   
          snapshot.Source = bitmapImage;
      }
  }

Silverlight supports both JPG and PNG images, and the image returned from Flash is PNG encoded. All we have to do on our side is turn the Base64 string into a byte array, load it into a MemoryStream, and then load the MemoryStream into a BitmapImage. A BitmapImage can be used as the source for any Silverlight Image control.

Now that we got the PNG image in Silverlight we can do what ever we want with it. We can upload it to the server, we can use it in Silverlight animations, or perhaps for cassual games like a sliding photo puzzle.

Summary

Both Silverlight and Flash have APIs to bridge between the plug-in and the browser. Using this you can make Flash and Silverlight talk, using JavaScript as the bridge between the technologies. I hope this tutorial has given you a good introduction to both Silverlight and Flash browser integration, and how it can be used to bridge the technologies. The webcam example shows how we can use this technique to achieve something quite interesting. This is by no mean a substitute for a proper webcam API in some future Silverlight version, but it gives you some basic webcam support you can play with. I will definitely be posting more about webcam support in Silverlight, and some examples of what you can use it for.

All the code is included with this article, and is licensed under the MS-Pl license. The article is copyrighted under the creative commons license.


Subscribe

Comments

  • -_-

    RE: Silverlight and Flash Interoperability using HTML Bridge and ExternalInterface API


    posted by Peter Strømberg on Sep 09, 2008 13:43

    What tricks does Silverlight offer the Flash Developer? Can you give an example of something I can do in Silverlight that I couldn't just do in Flash? 

  • -_-

    RE: Silverlight and Flash Interoperability using HTML Bridge and ExternalInterface API


    posted by Peter Strømberg on Oct 04, 2008 01:05

    Should I interpret the distinct lack of comments to mean "There are no tricks in Silverlight for the Flash Developer"? I can understand why you would want to piggyback the flash player capabilities, but if you can develop in Flash what advantage is there to bridging with Silverlight? The disadvantages of this approach are obvious and many. (or enlighten me!)

  • -_-

    RE: Silverlight and Flash Interoperability using HTML Bridge and ExternalInterface API


    posted by Eloff on Apr 29, 2009 01:14
    > Should I interpret the distinct lack of comments to mean "There are no tricks in Silverlight for the Flash Developer"?

    No, but I think others should interpret this as flame bait. Let me take the bait.

    Clearly there are areas Silverlight is better than Flash. The big one for me is developing in a language that doesn't suck. Not many people would seriously suggest that actionscript is on the same level as the CLR, with languages such as C# and IronPython. I suspect you're more of a designer than a developer, or you'd already know that (or maybe you have your head buried in the adobe sandbox.)

    Things you just can't do easily in flash? How about deep zoom? Reuse code from desktop applications? Port parts of an application from desktop to silverlight or visa versa? This is not like Adobe Air, let me calrify that I'm talking about fully functional and native desktop applications, not sandboxed desktop applications like Air enables (or SL 3 for that matter.)

    A big one is you can write real multithreaded code in Silverlight. Can you do that in Flash (I've heard not, but I haven't confirmed it.)

    Both platforms have their strengths and weaknesses, and right now Flash holds the upper hand in most places. However, in the long run I expect that situation to reverse.

     

  • -_-

    RE: Silverlight and Flash Interoperability using HTML Bridge and ExternalInterface API


    posted by Varun Srivastava on Jun 05, 2009 09:52

    Hi,

        I am looking for using the webcam in Silverlight technology and also I need to store the stream somewhere locally from some start and end point . Is it possible or where can I get the code for it ?

     

    Thanks

  • -_-

    RE: Silverlight and Flash Interoperability using HTML Bridge and ExternalInterface API


    posted by Grant on Aug 10, 2009 17:41

    Hi,

      can flash output video  and audio stream not snap?

       and how to do it?

     thx for help

  • AbdulJavedKhan

    RE: Silverlight and Flash Interoperability using HTML Bridge and ExternalInterface API


    posted by AbdulJavedKhan on Mar 19, 2010 07:25

    Hello

    I wonder if we can do copy/paste operation using same approach, that is using flash in silver light to enable user to perform copy/paste operation in silver light canvas because silver light doesn't support image copy option from clipboard(however we can copy text data from clipboard)

    Thanks,

    Abdul Javed Khan

Add Comment

Login to comment:
  *      *