28 June 2010
The RealEyes OSMF Player Sample (REOPS) project has a robust, extensible skinning system that includes a fully featured control bar component that you can extend. The REOPS project also includes some professionally designed templates from Juan Sanchez that offer the visual building blocks for the visual elements and allow for further customization.
This article is a follow-up of our previous article, Part 2: Building and configuration, where we showed you how to build a simple OSMF player and then explained each of these OSMF building blocks in simple terms. This article provides a deeper look into the skinning process and framework functionality of the REOPS external skin management solution. This article not only provides details and hands-on walkthroughs showing how to customize the look and feel of what is available out of the box, but also shows how to extend it with additional functionality.
The REOPS skinning system is broken up into a few different parts. They all work and sequence together to offer the most control and functionality.
The REOPS skinning system does not rely on the OSMF framework, other than referencing the OSMF MediaPlayer for events and media control, and the player display object shell (MediaContainer or MediaPlayerShell) for sizing and positioning. There are, of course, benefits and drawbacks to the skinning system's not directly implementing the OSMF framework for display, layout, or loading. Possible future enhancements to the REOPS project may include converting all or parts of the skinning system to a more OSMF-centric system, possibly even leveraging the plugin system.
The skinning system sequence starts with the main REOPS player being loaded and instantiated. The newly instantiated player then loads the external configuration XML file. This configuration file is the main source of all runtime configuration management. The configuration file specifies the following:
The main REOPS player loads all the configuration data and parses it into its model (PlayerConfig). All the
skinElement nodes of the configuration are parsed into SkinElement (com.realeyes.osmfplayer.model.config.skin.SkinElement) instances. Each skin element takes some base attributes used in the instantiation process:
The only required attributes are the
id and the
elementClass attributes. The skin element can also have any number of additional attributes aside from the base ones above that will be applied to the skin element. These attributes can be whatever the component defined by the
elementClassString attribute requires. Add those properties as attributes to the
skinElement node. Those attributes are parsed and stored in a Dictionary object called
properties on the com.realeyes.osmfplayer.model.config.skin.SkinElement class.
After the configuration data is loaded and parsed, the REOPS player goes through its initialization sequence, including the control bar container initialization (REOPS >
_initSkin()), which instantiates the SkinContainer component and applies the configuration details (see Figure 1).
The SkinContainer loads the specified external skin SWF file into the same application context as the main REOPS application (SkinContainer >
loadExternal()). Upon the completion of loading the external skin SWF file, the SkinContainer event handler (SkinContainer >
_skinLoadComplete()) loops through the
skinElements parsed from the configuration file. This is where things start to get complicated internally. As the SkinContainer loops through the elements, it dynamically generates instances from the skin SWF file based on the configuration
skinElement node parameters.
Here is the process the system goes through when generating skin elements:
altWidthThresholdare defined. The
altElementClassattribute defines the full class path of the skin element class to instantiate if the width of the media player shell is less than the value of the
altThresholdattributes are not defined, or if the size of the media player shell is greater than the threshold, the
elementClassattribute will be used to determine which class to instantiate.
buildSkinElement()method on the SkinElement class, creating the instance and dynamically applying all the attributes defined on the skin element's
initMethodNameproperty of the SkinElement. If there is, the method is dynamically invoked on the SkinContainer class and the instance is passed to the method as a parameter. This is very useful to set up event listeners and additional initialization that the newly created instances may require.
autoPositionhas a Boolean value of
true. If it does, the system then calls the SkinContainer's
updatePosition()method and passes the instance as a parameter. The
updatePosition()method handles dynamically positioning the SkinElement instance based on the data values set in the configuration file.
The following properties have control over the positioning, if defined in the config:
scaleMode property defines whether the element should be scaled based on the size of the media player shell. Currently the only supported value is
stretch, which will scale the SkinElement up or down to match the width of the media player shell while maintaining the proper aspect ratio.
vAdjust properties simply add the defined numeric values to the horizontal x position (
hAdjust) or the vertical y position (
vAdjust). Positive and negative numbers are supported. These properties can be used on their own or in conjunction with the alignment properties as well. The
vAlign properties control the positioning of the element in relation to the media player shell. If the value of
center, then the particular skin element would be placed in the middle of the video player shell, aligned to the right edge. All of the positioning properties/attributes are optional, and the default position is centered at the bottom.
An important note regarding the
autoPosition feature: When entering and returning from full-screen mode, if a skin element has the
autoPosition property set to
true, it will rerun the SkinContainer's
updatePosition() method for all those elements, repositioning them after the media player shell has been scaled to full screen, then rerunning the method on return from full-screen to return them to their original positions. This is true for all elements other than the instance set to the
_controlBar property via the
initControlBarInstance() method. That method would be invoked from the
initMethod attribute defined in the configuration file for the desired control bar skin element.
The skin samples/templates that come with the REOPS project have a few components that are ready to be implemented via skin elements and provide the building blocks to extend further. This section of the article will explore the different components, their usage, and purpose.
All the components within the skin templates extend the SkinElementBase class (com.realeyes.osmfplayer.controls.SkinElementBase). This class simply provides the similar properties used by the
The first and simplest component is the LoadingIndicator component (com.realeyes.osmfplayer.controls.LoadingIndicator). This is a simple loading spinner, and the only feature it contains other than purely timeline-driven visuals is the optional ability to set a label property to change the text it displays with the spinner. Like all the other skin elements provided, it extends the SkinElementBase and thus has the ability to use the
The ClosedCaptionField component is also a very simple class (com.realeyes.osmfplayer.controls.ClosedCaptionField). It is meant to be applied to a Flash MovieClip with a
cc_txt TextField instance and any other desired visuals. It is the visual display field for showing closed captioning. When the class instantiates, it sets its visibility to
false and has a
text() getter/setter method for setting the actual text displayed in the TextField.
The ControlBar component (com.realeyes.osmfplayer.controls.ControlBar) is the most complex in the template. In general, it is meant as a container for all of its optional subcomponents that you as the designer or developer may choose to implement. It is developed to be able to add or omit any of the predefined controls. To this end, it checks to see if one of the predefined controls exists. If so, it sets up the appropriate event handlers to catch the user interactions with the controls—such as clicking on a pause button or toggling the mute button. The ControlBar component also has some more advanced controls, such as the the volume bar and the progress bar, which the ControlBar uses to show progress, buffer length and the like. Both the ProgressBar (com.realeyes.osmfplayer.controls.ProgressBar) and the VolumeSlider (com.realeyes.osmfplayer.controls.VolumeSlider) are basic interactive controls with the needed functionality coded behind them.
Aside from those two controls, all the others are basic buttons, many with toggle capabilities. All the button-based controls either directly apply or extend the ToggleButton class (com.realeyes.osmfplayer.controls.ToggleButton). Those controls that do not need the toggle button functionality, such as a simple stop button, simply set the
toggle property to
false when they are initialized or when they are referenced in the ControlBar component (ControlBar >
_initListeners()). The ControlBar either passes interaction events from the buttons or bar components to the SkinContainer, or receives data from the SkinContainer to apply to the controls (progress, isLive, and disabling of controls as necessary, based on playback state). A minor but valuable note on the ControlBar component is that there is a width and height getter/setter that overrides the default width/height getter/setters of the MovieClip. It passes along the width and height of the
bg_mc MovieClip that is expected to be within the ControlBar component. This enables a more controllable sizing reference for the
autoPosition feature when items such as a vertical volume slider are in play.
On top of that, the ControlBar component can do smart resizing to scale the width of the bar without scaling the height and then reposition its child buttons appropriately. This smart resizing is triggered by setting the ControlBar's scaleMode property to "squeeze." During this resizing, the ControlBar class looks to see which controls are present and whether they are on the left or right side of the ControlBar instance. If the controls are left of center, they will be anchored to the left side; if the controls are right of center, they will be anchored to the right side. The ControlBar then resizes the background graphic to fit the width of the player and then repositions the controls based on their previous position in the ControlBar. The ProgressBar instance, if present, will be stretched or squeezed to fill the space between the controls on either side of it. Because of this, both the background graphic for the ControlBar and the ProgressBar symbol need to be set up with scale-9 slicing to ensure optimal appearance of the ControlBar as it resizes.
For this smart resizing to work, the control instances must be named the same. For instance, the background graphic must be
bg_mc. Only controls defined in the ControlBar class will be repositioned. However, the ControlBar class can be extended and adapted to reposition custom controls or graphics. You can also control how the individual controls are anchored by setting the
anchor property in the control's class definition. It can be set to "left," "right," or "both." Setting the property to "both" will cause it to be stretched like the ProgressBar. Look at the ProgressBar class to see how this is implemented.
Note: In the skin templates, the classes are applied to the MovieClips in the Library Panel via the Advanced view of the Symbol Properties window, and are applied directly as the Class for the Linkage. This process has the benefit of directly using the defined class instead of subclassing it and generating additional classes dynamically, as would happen if a class were applied to the Base Class field instead. However, since the class is applied to the Class field instead of the Base Class field, this process has the drawback of not allowing any frame scripts to be applied to the components. This means that if you desire to use any frame scripts, you should switch the defined class to the Base Class field and supply a new class name in the Class field that will be dynamically created. Be sure to use whatever the value is in the Class field in the XML configuration file when you want to apply the component or MovieClip as a SkinElement.
The glue that holds all the elements together actually invokes the displays and controls the media is the SkinContainer. With the included elements, the SkinContainer has the definitions for the
initMethods of each SkinElement. Those
initMethods create the event handlers and set up the proper display and initialization properties, and then maintain the links between the media playback and the visual elements. A key example of this is how the ControlBar controls the media playback. All of this is done by simply broadcasting events such as the ControlBarEvent with a type of ControlBarEvent.PAUSE from the ControlBar skin element. The SkinContainer listens for such events, and then calls the appropriate method (example:
pause()) on the media player.
To add new custom skin elements into the deployment, the process is fairly simple. Add the visual elements to the skin FLA file and link them to any code they need by providing them with a Class or Base Class. Any class for your component should extend SkinElement. Then add the appropriate
skinElement node to the XML configuration file. If your new element requires integration with the media player or other UI controls, then you will likely want to extend the SkinContainer with a custom class and create the appropriate one or more
initMethods for your skin elements. Make sure the custom extension of the SkinContainer is used in the REOPS main class—either by extending or modifying that class—and that should be it. We will publish a fully detailed, step-by-step hands-on article later showing exactly how to do this.
Having a high-impact visual display for a video player is an ever-growing request. The REOPS project has attempted to provide an effective visual skinning system, backed by the ability to add custom functionality and leverage the power of the OSMF. The visual skins provided offer great, professional-quality visuals that you are welcome to use out of the box, customize to make your own, or simply use as a referenced starting point for building your own skin from the ground up. Really, the only requirement for the most complex provided skin element (the control bar) is that the instances have the right names, and that the main component implements or extends the default ControlBar class. The visual side is open to whatever the world desires and your creativity can imagine.
The skin templates are installed as Flash templates with the REOPS Flash Extension (MXP) available from the REOPS project page. They are accessed in the initial start splash screen > Create from Template > OSMF_Skins or via the File > New > Templates (tab on top) > OSMF_Skins (see Figure 2)
The templates themselves are broken out into two main parts, Compositions and Assets (see Figure 3).
Compositions are the actual SkinElements and are technically component MovieClips with classes defined for the linkages. In the provided templates this includes the LoadingIndicator, ClosedCaptionField, and the ControlBar. One of the major benefits of the skin templates with the REOPS project is that you can actually compile and see the animations and user interactions of the compositions independent of the player. This allows for true layout testing, user interaction, and motion review before deploying. This is in part because the code for the aforementioned skin elements is actually supplied as a compiled clip within the library of the skin templates. The code is also in the REOPS project if you prefer to work directly with the source and map the class-path of the FLA to the REOPS source. The concept of the compositions is they contain the different individual controls, or assets.
Whichever assets are implemented by a composition is completely up to the designer and developer. It is as simple as copying and pasting the asset into a composition and positioning it appropriately on the timeline—or via ActionScript if you prefer and understand the process. In essence, the Assets section of the template can be viewed as the preparation section. In the Assets section, you can simply double-click any item to edit its visual source or sources. If that asset is already in the composition, then you will see it update appropriately there as well. Similarly, if it is already in the composition, you can edit directly from the composition and see it update in the assets. if the asset doesn't exist in the composition initially, all you have to do is copy and paste the asset from the below section directly into one of the compositions. Since all the assets on the Stage are already applied with the appropriate instance names, they should work automatically for you when applied to an appropriate skin element such as the ControlBar.
It is important to note that these instance names should not be changed, and cannot be nested deeper than direct children of such components unless you update the code appropriately for your use case. All of the assets in the skin templates are on a layer set as a guide and thus will not appear when you compile the skin. This is done on purpose to not detract from the compositions when testing, as the compositions are the only items available to be used by default from the skin SWF files via the XML configuration system.
Juan Sanchez, who designed the skin templates around the base we provided, wrote an excellent article, OSMF Player Skins, describing his process for the design and implementation. In general, the recommended approach for skinning is easy: First pick a template that matches the basic direction you want to follow; then modify the assets as you see fit.
The skinning system from the RealEyes OSMF Player Sample project offers a lot of functionality out of the box, and both visual and functional extensibility. This article has offered you some insight into the concepts, capabilities, structure, and sequence in which the skinning system works. The key player in managing the skin elements is the SkinContainer; refer to the SkinContainer class for a deeper dive into the system.
In a future article, we will dive into a hands-on approach of creating customized skins and adding new functionality to the skin and player. Check back often on the REOPS code page for more samples and enhancements, too.