• warning: Parameter 1 to tac_lite_node_grants() expected to be a reference, value given in /home/rbezemer/softwarebyrichard.com/includes/module.inc on line 483.
  • warning: Parameter 2 to gmap_gmap() expected to be a reference, value given in /home/rbezemer/softwarebyrichard.com/includes/module.inc on line 483.
  • warning: Parameter 1 to tac_lite_node_grants() expected to be a reference, value given in /home/rbezemer/softwarebyrichard.com/includes/module.inc on line 483.
  • warning: Parameter 1 to tac_lite_node_grants() expected to be a reference, value given in /home/rbezemer/softwarebyrichard.com/includes/module.inc on line 483.
  • warning: Parameter 1 to tac_lite_node_grants() expected to be a reference, value given in /home/rbezemer/softwarebyrichard.com/includes/module.inc on line 483.
  • warning: Parameter 1 to tac_lite_node_grants() expected to be a reference, value given in /home/rbezemer/softwarebyrichard.com/includes/module.inc on line 483.
  • warning: Parameter 1 to tac_lite_node_grants() expected to be a reference, value given in /home/rbezemer/softwarebyrichard.com/includes/module.inc on line 483.
  • warning: Parameter 1 to tac_lite_node_grants() expected to be a reference, value given in /home/rbezemer/softwarebyrichard.com/includes/module.inc on line 483.

Flexing the Youtube Chromeless player

Getting youtube and flex / flash to work together can be a nightmare. Our original implementation ended up having us being forced to do "hack" youtube  to tease out the path the the flv and play that in a native flex video player interface. This solution works well and lets you have multiple videos playing inside your flex application at once, but unfortunatly youtube doesn't like this idea at all. Over the last few months they have continuously been changing their paths to the flv so that any html scraping code to pull out the path is essentially worthless, unless you really like rewriting html parsing code every few days, we needed a better solution.

Youtube recommends using their chromeless player api . This player is great, you can embed it in flash and send it all types of commands with your own custom interface. However there is a catch... it's written in ActionScript 2. I don't need to go into the details of the problems of embedding as2 into as3 code as it's documented all over the web, but basically this means we can't use a nice stylish flex ui to send commands to the embedded player. The solution is not too complicated, we need to write an ActionScript 2 wrapper that uses a local connection object between the ActionScript 2 swf and the ActionScript 3 swf that it's embedded in.

The code isn't to complicated but it definitely takes a little bit of getting used to get your head around the local connection methods. There are a few third party api's out there already that do most of the heavy lifting. The one I found most usefull was TubeLoc which seems to be youtube's recommended embedding solution for Flex and ActionScript 3. However there was one massive limitation for our project... you can't embed more than one video in your flash file at one time. This has to do with the id's of the local connection object, and the complex communications methods that are used with them. You can see the list of problems people are having with it here , and tubeloc is by far the best of the third party solutions I could find.

Needless to say, I found a few out of the way forum posting that pointed me in the right direction an came up with this solution. One of the key fixes involves making sure you are only creating and initializing one local connection object at a time, if you start doing multiple ones at once, bad things start to happen. It's a nearly fully featured youtube embedding library. If you need more functionality from the as2 wrapper, you will need a way to compile as2 code. I just used the trial version of Flash CS4 to build the as2 swf that is loaded by the flex app.

Anyway here's the flex app, view source is here.

You are missing some Flash content that should appear here! Perhaps your browser cannot display it, or maybe it did not initialize correctly.

and here is the as2 code that gets compiled into ytPlayer.swf. I can't take all the credit for it. I mostly just extended the code provided by http://yikulju.com/blog/?p=197. Most of the loading code is his, I just extended it to enable most of the advanced features of the youtube chromless player (such as video progress reporting, volume, etc...). Main thing to note is there are bugs with the volume controls when there are multiple videos, but this seems to be a youtube chromeless player bug and not a flex / flash bug.

  1. System.security.allowDomain('www.youtube.com');
  2. System.security.allowDomain('gdata.youtube.com');
  3. System.security.allowInsecureDomain('gdata.youtube.com');
  4. System.security.allowInsecureDomain('www.youtube.com');
  5.  
  6. var loadInterval:Number;
  7. var progressInterval:Number;
  8. var ytplayer:MovieClip = this.createEmptyMovieClip("ytplayer", this.getNextHighestDepth());
  9.  
  10. var swf:String = "http://www.youtube.com/apiplayer";
  11.  
  12.  
  13. var channel:String = _root.boxName;
  14. var channelBack:String = _root.boxName+"Back";
  15. var lastStatus:Number = -1;
  16. cn.connect(channel);
  17.  
  18. ytPlayerLoaderListener = {};
  19. ytPlayerLoaderListener.onLoadInit = function() {
  20. loadInterval = setInterval(checkPlayerLoaded, 250);
  21. };
  22.  
  23. function checkPlayerLoaded():Void {
  24. if (ytplayer.isPlayerLoaded()) {
  25. clearInterval(loadInterval); // IMPORTANT - kill the interval
  26.  
  27. ytplayer.addEventListener("onStateChange",onPlayerStateChange);
  28. ytplayer.addEventListener("onError",onPlayerError);
  29. loadIndicator._visible = false;
  30. progressInterval = setInterval(dispatchMovieProgress, 500);
  31.  
  32. cn.send(channelBack,"onPlayerLoaded");
  33. }
  34. }
  35. //This reports the movie's state changes back to flex (playing, paused, etc..)
  36. function onPlayerStateChange(newState:Number) {
  37. //do something when player changes state
  38. if(newState!=lastState)
  39. {
  40. lastState = newState;
  41. var event:Object = {eventName:"onStateChange",
  42. value:lastState,
  43. duration:ytPlayer.getDuration()/*,
  44. isMuted:ytPlayer.isMuted(),
  45. volume:ytPlayer.getVolume(),
  46. */};
  47. cn.send(channelBack,"onStateChange",event);
  48. }
  49. }
  50.  
  51. function onPlayerError(errorCode:Number) {
  52. //do something on player error
  53. cn.send(channelBack,"onError");
  54. }
  55. //Tell flex every few milliseconds that the movie playhead has changes
  56. function dispatchMovieProgress():Void {
  57. if(lastState==1) {
  58. var event:Object = {eventName:"onMovieProgress", currentTime:ytplayer.getCurrentTime(), duration:ytplayer.getDuration()};
  59. cn.send(channelBack,"onMovieProgress",event);
  60. }
  61. }
  62.  
  63. var ytPlayerLoader:MovieClipLoader = new MovieClipLoader();
  64. ytPlayerLoader.addListener(ytPlayerLoaderListener);
  65. ytPlayerLoader.loadClip(swf,ytplayer);
  66.  
  67. cn.pauseVideo = function() {
  68. ytplayer.pauseVideo();
  69. };
  70. cn.playVideo = function() {
  71. ytplayer.playVideo();
  72. };
  73. cn.stopVideo = function() {
  74. ytplayer.stopVideo();
  75. };
  76. cn.loadVideoById = function(id) {
  77. ytplayer.loadVideoById(id,0);
  78. };
  79. cn.destroy = function() {
  80. ytplayer.destroy();
  81. };
  82. cn.cueVideoById = function(id) {
  83. ytplayer.cueVideoById(id,0);
  84. };
  85. cn.seekTo = function(dataObject_p:Object) {
  86. ytplayer.seekTo(dataObject_p.seconds,dataObject_p.allowSeekAhead);
  87. };
  88. cn.setVolume = function(dataObject_p:Object) {
  89. ytplayer.setVolume(dataObject_p.volume);
  90. };
  91. cn.unMute = function() {
  92. ytplayer.unMute();
  93. dispatchMovieProgress();
  94. };
  95. cn.mute = function() {
  96. ytplayer.mute();
  97. dispatchMovieProgress();
  98. };
  99. cn.setSize = function(dataObject_p:Object) {
  100. ytplayer.setSize(dataObject_p.width,dataObject_p.height);
  101. };

Your rating: None Average: 2.9 (40 votes)