Simplifying StageVideo with StageVideoProxy


Adobe recently took a large step in changing the way video gets rendered in the Flash player. For the most part the player has always taken a large role in rendering video to the screen. With the release of Flash player 10.2 this has all changed. Now if you explicitly tell the player to, you can offload the video completely to the hardware which decreases CPU usage (literally to 0%), lowers memory usage, enables higher frame rates, and overall enables greater pixel fidelity and video quality.

Obviously with all the upcoming phones, tablets, and connected TVs this has an even higher impact on a video applications experience. At Dreamsocket we've been fortunate enough to be an early adopter with projects on these platforms. From developing video player's tailored for Android phones to creating Cartoon Network and Adult Swim's GoogleTV apps, we've been able to see the impact this new change has, in addition to figuring out the best way to integrate it into existing applications.

All that said, the one thing that really struck us was how unique the new StageVideo API is. I say unique because it is a completely new API and is completely different than working with the Video object. I understand the differences, however do you really need to force someone to go back and recode everything just to use it? I know it forces a user to explicitly think and spend time putting StageVideo into an app, but why? It should be EASY. I mean really EASY.

Enter StageVideoProxy. We wanted to go into all of our video applications and simply swap out a Video object reference for a new Class that would use StageVideo if present and fallback to using a standard Video object if it failed. By having a simple class that extended Video, we could just swap out the instance and proxy all the calls to StageVideo when it was available. This made it EXTREMELY easy to retro fit any existing application with StageVideo functionality. In addition, we kept all 10.2 API references in places that would not get called in players below 10.2, making the code backwards compatible.

Super right? Well we did find a few limitations. First, StageVideo doesn't really have a way to clear the video, so we had set the size to 0 width and 0 height to "clear" it. Second, we don't really know depth of display objects vs other display objects easily, so we don't have a way to determine z depth for the videos. The second step doesn't really matter though, given the implementation currently only supports a single StageVideo instance. I'd like to add support for multi videos, but I'm still unclear on how many I can have and what dictates that. I understand how to know when they are there and when you hit your limit, but I'd still like to fully understand it before I add it in.

Regardless, this is super useful code so merry nerdmas! Here you go in all its MIT licensed glory

Actionscript:
  1. /**
  2. * Copyright (c) 2010 Dreamsocket Incorporated.
  3. * All rights reserved.
  4. *
  5. * Permission is hereby granted, free of charge, to any person
  6. * obtaining a copy of this software and associated documentation
  7. * files (the "Software"), to deal in the Software without
  8. * restriction, including without limitation the rights to use,
  9. * copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. * copies of the Software, and to permit persons to whom the
  11. * Software is furnished to do so, subject to the following
  12. * conditions:
  13. *
  14. * The above copyright notice and this permission notice shall be
  15. * included in all copies or substantial portions of the Software.
  16. *
  17. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  18. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  19. * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  20. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  21. * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  22. * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  23. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  24. * OTHER DEALINGS IN THE SOFTWARE.
  25. **/
  26.  
  27.  
  28. package com.dreamsocket.media
  29. {
  30.     import flash.events.Event;
  31.     import flash.events.StageVideoEvent;
  32.     import flash.events.StageVideoAvailabilityEvent;
  33.     import flash.events.VideoEvent;
  34.     import flash.geom.Rectangle;
  35.     import flash.media.StageVideo;
  36.     import flash.media.StageVideoAvailability;
  37.     import flash.media.Video;
  38.     import flash.net.NetStream;
  39.    
  40.     public class StageVideoProxy extends Video
  41.     {
  42.         protected var m_netStream:NetStream;
  43.         protected var m_stageVideo:Object;
  44.        
  45.        
  46.         public function StageVideoProxy (p_width:int = 320, p_height:int = 240)
  47.         {
  48.             super(p_width, p_height);
  49.  
  50.             this.addEventListener(Event.ADDED_TO_STAGE, this.onAddedToStage);
  51.             this.addEventListener(Event.REMOVED_FROM_STAGE, this.onRemovedFromStage);
  52.         }
  53.    
  54.    
  55.         override public function set height(p_value:Number):void
  56.         {
  57.             if(p_value != this.height)
  58.             {
  59.                 super.height = p_value;
  60.                 this.layoutView();
  61.             }
  62.         }
  63.        
  64.        
  65.         override public function get videoHeight():int
  66.         {
  67.             return this.m_stageVideo ? this.m_stageVideo.videoHeight : super.videoHeight;
  68.         }
  69.        
  70.                
  71.         override public function get videoWidth():int
  72.         {
  73.             return this.m_stageVideo ? this.m_stageVideo.videoWidth : super.videoWidth;
  74.         }
  75.    
  76.            
  77.         override public function set width(p_value:Number):void
  78.         {
  79.             if(p_value != this.width)
  80.             {
  81.                 super.width = p_value;
  82.                 this.layoutView();
  83.             }
  84.         }
  85.        
  86.                
  87.         override public function set x(p_value:Number):void
  88.         {
  89.             if(p_value != this.x)
  90.             {
  91.                 super.x = p_value;
  92.                 this.layoutView();
  93.             }
  94.         }
  95.        
  96.        
  97.         override public function set y(p_value:Number):void
  98.         {
  99.             if(p_value != this.y)
  100.             {
  101.                 super.y = p_value;
  102.                 this.layoutView();
  103.             }
  104.         }
  105.  
  106.        
  107.         override public function attachNetStream(p_stream:NetStream):void
  108.         {
  109.             if(p_stream != this.m_netStream)
  110.             {
  111.                 this.m_netStream = p_stream;
  112.                 this.teardownStream();
  113.                 this.setupStageVideo();
  114.             }
  115.         }
  116.        
  117.         protected function setupSpriteVideo():void
  118.         {      
  119.             this.m_stageVideo = null;
  120.             super.attachNetStream(this.m_netStream);       
  121.         }
  122.        
  123.        
  124.         protected function setupStageVideo():void
  125.         {   // only setup the view when video is on stage and there is a netstream attached
  126.             // this helps prevent as much as possible the time when a StageVideo is initialized
  127.             if(!this.stage || !this.m_netStream) return;
  128.            
  129.             try
  130.             {
  131.                 if(!this.m_stageVideo && this.stage.stageVideos.length>= 1)
  132.                 {
  133.                     this.m_stageVideo = this.stage.stageVideos[0];
  134.                     this.m_stageVideo.addEventListener(StageVideoEvent.RENDER_STATE, this.onRenderStateChanged);
  135.                     this.layoutView();
  136.                 }
  137.                
  138.                 if(this.m_stageVideo)
  139.                 {
  140.                     this.m_stageVideo.attachNetStream(this.m_netStream);
  141.                 }
  142.                 else
  143.                 {
  144.                     this.setupSpriteVideo();
  145.                 }
  146.             }
  147.             catch(error:Error)
  148.             {
  149.                 this.setupSpriteVideo();
  150.             }
  151.         }
  152.        
  153.        
  154.         protected function teardownStream():void
  155.         {   
  156.             try
  157.             {
  158.                 if(this.m_stageVideo)
  159.                 {
  160.                     this.m_stageVideo.viewPort = new Rectangle(this.x, this.y, 0, 0);
  161.                     this.m_stageVideo.attachNetStream(null);
  162.                 }
  163.                 else if(this.m_netStream)
  164.                 {
  165.                     super.attachNetStream(null);
  166.                     this.clear();
  167.                 }
  168.                
  169.             }
  170.             catch(error:Error){}
  171.         }   
  172.        
  173.            
  174.         protected function layoutView():void
  175.         {
  176.             if(this.m_stageVideo)
  177.                 this.m_stageVideo.viewPort = new Rectangle(this.x, this.y, this.width, this.height);
  178.         }
  179.  
  180.        
  181.         protected function onAddedToStage(p_event:Event):void
  182.         {
  183.             //this.stage.addEventListener(StageVideoAvailabilityEvent.STAGE_VIDEO_AVAILABILITY, this.onStageVideoAvailabilityChanged);
  184.             this.setupStageVideo();
  185.             this.layoutView();
  186.         }
  187.        
  188.        
  189.         protected function onRemovedFromStage(p_event:Event):void
  190.         {   
  191.             //this.stage.removeEventListener(StageVideoAvailabilityEvent.STAGE_VIDEO_AVAILABILITY, this.onStageVideoAvailabilityChanged);
  192.             this.teardownStream();
  193.         }
  194.        
  195.                
  196.         protected function onRenderStateChanged(p_event:Event):void
  197.         {
  198.             switch(StageVideoEvent(p_event).status)
  199.             {
  200.                 case VideoEvent.RENDER_STATUS_UNAVAILABLE:
  201.                     this.teardownStream();
  202.                     this.setupStageVideo();
  203.                     break;
  204.             }
  205.         }
  206.        
  207.        
  208.         protected function onStageVideoAvailabilityChanged(p_event:Event):void
  209.         {
  210.             this.teardownStream();
  211.             this.setupStageVideo();
  212.         }
  213.     }
  214. }

  1. No comments yet.
(will not be published)