By David Hassoun
 
By John Crosby
 
Created
26 July 2010
 

Requirements

 
Prerequisite knowledge

You should be familiar with the Flash Platform, building applications in Flash Professional or Flash Builder, using ActionScript 3, and concepts related to playing video using Flash.
 

 
User level

Advanced
 

 
Sample files

This continues the three-part series on developing media players and experiences with Open Source Media Framework (OSMF). This article continues from Part 1 and focuses on adding UI controls and extending the media application capabilities with the use of sequential (also known as serial) and parallel complex compositions.
 
This article dives into the complex layout and container functionality that OSMF offers so that you as the developer or designer can take full control over the visual experience. The four walkthroughs contained here will show you how to use the advanced layout controls offered by OSMF components and how to control their layout dynamically, how to create a sequence of media items, how to use the ParallelElement to display two MediaElement instances at the same time, and finally, how to display composition-based media elements via the layout features of OSMF.
 

 
Walkthrough 4: UI control bar and layout management

This walkthrough shows you how to use the advanced layout controls offered by OSMF components and how to control the layout dynamically of interactive elements outside of OSMF components, conditionally based on the size of the content displayed by OSMF. Specifically, you will do the following:
 
  • Use the MediaContainer advanced layout capabilities with metadata for composite media loading and display
  • Integrate custom graphical user interface controls to make a skinnable control bar
  • Make the control bar functional by linking user interactions with the MediaPlayer API and adjusting the position of the control bar dynamically, based on the media content being displayed via events.
 
Objectives
  • Understand how to utilize MediaElements (specifically the ImageElement object or other forms of Loadable elements) to create more advanced media display experiences.
  • Understand how to use LayoutMetaData with a MediaElement object to control how it will be displayed in a MediaContainer (or MediaPlayerSprite).
  • Implement manual loading of MediaElements via the use of traits (LoadTrait) when not controlled by a MediaPlayer.
  • Display and link user controls with the MediaPlayer to create custom control bars, including basic play, pause, and real-time visual progress.
  • Dynamically adjust the position of non-OSMF instances (the control bar) relatively to the size of the media being displayed.
 
Setup
  1. Open the file WT04_IntegratingUIControls.as in the {SAMPLES_PROJECT}/src directory.
    Note: This file has been provided as a starting point for these walkthroughs.
     
  2. Set the class file as the application file to compile. There are two different ways of doing this, depending on which program you using to build your application:
    Flash Builder: Right-click WT04_IntegratingUIControls.as and select Set as Default Application from the context menu that appears. This will add the project to the list of compilable applications. A blue dot on the file icon indicates that the file is the default application file.
     
    Flash Professional: Open OSMF_SampleTemplate.fla and save it as WT04_IntegratingUIControls.fla. Change the document class for the file (in the Properties panel) to WT04_IntegratingUIControls.
     
 
Add an image overlay (bug)
Locate the comment that begins with "//Marker 1:".
 
 
Create and set up the image for the bug
  1. Under the comment, create a local URLResource variable named bugUrlResourceg. Set bugUrlResourceg equal to a new URLResource object and pass the String "assets/osmf_stacked.png to the constructor:
//Marker 1: Add Bug Overlay var bugUrlResource:URLResource = new URLResource( "assets/osmf_stacked.png");
  1. Create a local ImageElement variable named bug. Set bug equal to a new ImageElement and pass the bugUrlResource variable to the constructor:
var bugUrlResource:URLResource = new URLResource( "assets/osmf_stacked.png"); var bug:ImageElement = new ImageElement( bugUrlResource );
 
Manage the layout and positioning of the bug with metadata
  1. Create a local LayoutMetadata variable named layoutData and set it equal to a new LayoutMetadata object:
var bug:ImageElement = new ImageElement( bugUrlResource ); var layoutData:LayoutMetadata = new LayoutMetadata();
  1. Set the right property of the layoutData object equal to 10:
var layoutData:LayoutMetadata = new LayoutMetadata(); layoutData.right = 10;
  1. Set the bottom property of the layoutData object equal to 10:
var layoutData:LayoutMetadata = new LayoutMetadata(); layoutData.right = 10; layoutData.bottom = 10;
  1. Set the scaleMode property of the layoutData object equal to the static property NONE of the ScaleMode object:
var layoutData:LayoutMetadata = new LayoutMetadata(); layoutData.right = 10; layoutData.bottom = 10; layoutData.scaleMode = ScaleMode.NONE;
  1. Add the LayoutMetadata to the bug ImageElement by calling the addValue() method on the metaData property of the bug element. Pass the static LAYOUT_NAMESPACE property of the LayoutMetadata object as the first parameter and the layoutData object as the second parameter:
layoutData.scaleMode = ScaleMode.NONE; bug.metadata.addValue( LayoutMetadata.LAYOUT_NAMESPACE, layoutData );
 
Load the bug by accessing the appropriate trait and display the bug
Since the bug ImageElement isn't going to be used with a MediaPlayer to handle the loading and such, you must manage the loading manually via the LoadTrait of the ImageElement object:
 
  1. Create a local LoadTrait variable named bugLoadTrait.
  2. Set bugLoadTrait equal to the result of calling getTrait() on the bug object. To specify the type of trait to load, pass the static LOAD property of the MediaTraitType object to the constructor, making sure to cast the result as a LoadTrait:
bug.metadata.addValue( LayoutMetadata.LAYOUT_NAMESPACE, layoutData ); var bugLoadTrait:LoadTrait = bug.getTrait( MediaTraitType.LOAD ) as LoadTrait;
  1. Call the load() method on the bug object to load the image:
var bugLoadTrait:LoadTrait = bug.getTrait( MediaTraitType.LOAD ) as LoadTrait; bugLoadTrait.load();
  1. Add the bug instance to the MediaContainer instance called container:
container.addMediaElement( bug );
The completed code should look like the following:
 
//Marker 1: Add Bug Overlay var bugUrlResource:URLResource = new URLResource( "assets/osmf_stacked.png" ); var bug:ImageElement = new ImageElement( bugUrlResource ); var layoutData:LayoutMetadata = new LayoutMetadata(); layoutData.right = 10; layoutData.bottom = 10; layoutData.scaleMode = ScaleMode.NONE; bug.metadata.addValue( LayoutMetadata.LAYOUT_NAMESPACE, layoutData ); var bugLoadTrait:LoadTrait = bug.getTrait( MediaTraitType.LOAD ) as LoadTrait; bugLoadTrait.load(); container.addMediaElement( bug );
  1. Save the file and run the application. You should see the bug image of the OSMF logo in the bottom right of the screen (see Figure 1). Since the layoutData used the anchor properties of right and bottom, no matter what the size of the media played, the bug will always be 10 pixels from the right and the bottom.
Figure 1. Media playback with image overlay
Figure 1. Media playback with image overlay
 
Implement a control bar without the OSMF layout containers
  1. Locate the comment that starts "//Marker 2:" in the constructor. Add a call to the initControlBar() method":
//Marker 2: Initialize Control Bar initControlBar();
  1. Locate the initControlBar() method. Review the content of this method. This method creates and positions the control bar elements and adds listeners for play, pause, and seek events. The visual elements are being instantiated from a SWC file in the libs folder that was created from a Flash (FLA) source file located in the libs directory as well.
  2. Save the file and run the application. You should see the control bar and the bug image and a video should begin to play (see Figure 2). The control bar isn't functional yet; you'll add in the control bar interaction and positioning next.
Figure 2. Initial custom control bar integration
Figure 2. Initial custom control bar integration
 
Add the control bar interactions
  1. Locate the comment that begins "//Marker3:" in the _onPauseClick() event handler method. If you remember from the initControlBar() method, this is the event handler for the pause button. Under the comment, call the pause() method on the player object:
protected function _onPauseClick( p_evt:MouseEvent ):void { //Marker 3: Add call to pause method player.pause(); }
  1. Locate the event handler method for the play button, _onPlayClick(). Under the comment that begins "//Marker 4:", call the play() method on the player object:
protected function _onPlayClick( p_evt:MouseEvent ):void { //Marker 4: Add call to pause method player.play(); }
  1. Save and run the application. The control bar should now be positioned under the video and the pause and play buttons should also work. The progress bar, however, doesn't reflect the correct position and you cannot seek.
 
Automate the positioning of the control bar to the layout container
To activate automatic positioning and the progress bar, you will need to set up event handler methods for the mediaSizeChange and currentTimeChange events on the player object:
 
  1. Locate the comment that begins "//Marker 5:". Under the comment, add an event listener to the player object for the static MEDIA_SIZE_CHANGE property of the DispayObjectEvent class and provide the _onSizeChange function as the handler:
//Marker 5: Add MediaPlayer listeners for media size and current time change player.addEventListener( DisplayObjectEvent.MEDIA_SIZE_CHANGE, _onSizeChange );
  1. Add an event listener for the static CURRENT_TIME_CHANGE property of the TimeEvent class and provide the _onProgress function as the handler:
//Marker 5: Add MediaPlayer listeners for media size and current time change player.addEventListener( DisplayObjectEvent.MEDIA_SIZE_CHANGE, _onSizeChange); player.addEventListener( TimeEvent.CURRENT_TIME_CHANGE, _onProgress );
  1. Locate the comment that begins "//Marker 6:" in the _onSizeChange() method. Under the comment, set the y property of the controlBar object equal to the newHeight property of the p_evt object. Figure 3 shows the result in the player:
//Marker 6: Reposition appropriately when the media size changes controlBar.y = p_evt.newHeight;
Figure 3. Completed custom control bar implementation
Figure 3. Completed custom control bar implementation
 
Activate the active progress bar display and seeking
  1. Locate the comment that begins "//Marker 7:" in the _onProgress() event handler method. Under the comment, set the scaleX property of the progressCurrent object equal to the time property of the p_evt object divided by the duration property of the player object:
//Marker 7: Set the scalex of the current progress bar to the current time divided by the duration of the media progressCurrent.scaleX = p_evt.time / player.duration;
  1. Save the file and run the application. The progress bar should now reflect the media's current position (see Figure 4).
Figure 4. Progress bar showing playback progress
Figure 4. Progress bar showing playback progress
  1. Locate the comment that begins "//Marker 8:" in the _onSeek() method. Under the comment, create a new local Number variable named seekTo and set it equal to the duration property of the player object multiplied by the result of dividing the mouseX property of the p_evt object's target property by the width of the p_evt object's target property:
//Marker 8: Call the seek method of the MediaPlayer passing it the appropriate time determined by the click on the progress track var seekTo:Number = player.duration * ( p_evt.target.mouseX/p_evt.target.width );
  1. Call the seek() method on the player object, passing it the seekTo variable:
//Marker 8: Call the seek method of the MediaPlayer passing it the appropriate time determined by the click on the progress track var seekTo:Number = player.duration * ( p_evt.target.mouseX/p_evt.target.width ); player.seek( seekTo );
  1. Save the file and run the application. You should now be able to seek by clicking  the progress bar.
This walkthrough gave you an understanding of how to use the OSMF layout capabilities via metadata, and also how dynamically to lay out items outside of OSMF, but relative to what's happening, providing you many of the basic layout skils needed to extend your own visual experience. The next walkthrough starts down the path of complex media compositions. With compositions, playlists and advanced media merging become easy implementable tasks with OSMF.
 

 
Walkthrough 5: Basic serial elements

This walkthrough creates a sequence of media items to create an interactive movie experience complete with a progressive video pre-roll, a dynamic streaming preview, a SWF file–based quiz, and, if passed, access to watch the whole clip via Dynamic Streaming. This is achieved by using the SerialElement object to create the sequence of MediaElements and the StreamingURLResouce object to create a sub-clip of a streaming media item. Then, based on user interaction, you can completely overwrite the current media being displayed (the SerialElement) and play "unlocked" content.
 
 
Objectives
  • Create a SerialElement object and add different types of media to it.
  • Create a sub-clip of a streaming item by use of the StreamingURLResource class.
  • Use the metaData property and a LayoutMetadata object to make the display consistent across the media types.
 
Setup
  1. Open the file WT05_SerialCompositions.as in the {SAMPLES_PROJECT}/src directory.
    Note: This file has been provided as a starting point for these walkthroughs.
     
  2. Set the class file as the application file to compile. There are two different ways of doing this, depending on which program you are using to build your application:
    Flash Builder: Right-click WT05_SerialCompositions.as and select Set as Default Application from the context menu that appears. This will add the project to the list of compilable applications. A blue dot on the file icon indicates that the file is the default application file.
     
    Flash Professional: Open OSMF_SampleTemplate.fla and save it as WT05_SerialCompositions.fla. Change the document class for the file (in the Properties panel) to WT05_SerialCompositions.
     
 
Work with serial elements
Please note that two new static constant variables have been added: LOGO_VID and QUIZ_SWF.
 
  1. Locate the comment that begins with "//Marker 1:" in the initPlayer() method. Under the comment, create a new local MediaElement variable named preRoll and set this equal to the result of calling the createMediaElement() method on the mediaFactory object. Make sure to create a new URLResource object using the static const LOGO_VID variable:
//Marker 1: create a video element for the pre-roll video - use the newly added LOGO_VID const var preRoll:MediaElement = mediaFactory.createMediaElement( new URLResource( LOGO_VID ) );
  1. Under the comment starting "//Marker 2:", create a new local StreamingURLResource variable named resourceStart.
    The StreamingURLResource class extends the URLResource class, which we have used often before. StreamingURLResource can be used when playing back streaming content, such as RTMP-based content streamed from a Flash Media Server or Flash Video Streaming CDN service. The StreamingURLResource class allows for some additional control over how the content is handled.
     
  2. Set resourceStart equal to a new StreamingURLResource object. Pass the const DYNAMIC_STREAMING variable as the first parameter (the URI), the static RECORDED property of the StreamType object as the second parameter (which says the stream is a recorded VOD stream), zero (0) as the third (which indicates to start the stream at the beginning, or 0 seconds), and twenty (20) as the last parameter (which defines how much of the stream to play). This will create a sub-clip of the first 20 seconds of the video stream:
//Marker 2: Create a StreamingURLResource to create a sub clip of the streaming resource - play from 0-20 sec var resourceStart:StreamingURLResource = new StreamingURLResource( DYNAMIC_STREAMING, StreamType.RECORDED, 0, 20 );
  1. Under the "//Marker 3:" comment, create a local MediaElement variable named element and set it equal to the a new MediaElement object created using the mediaFactory and resourceStart objects:
     
//Marker 3: Create the media element from the streaming resource sub clip var element:MediaElement = mediaFactory.createMediaElement( resourceStart );
  1. Under the "//Marker 4:" comment, create a new local MediaElement variable named quiz. Use the static const QUIZ_SWF as the path for the new MediaElement:
     
//Marker 4: Create another post roll clip that is an interactive SWF piece. var quiz:MediaElement = mediaFactory.createMediaElement( new URLResource( QUIZ_SWF ) );
  1. After the "//Marker 5:" comment, add an event listener on the this object for the "correct" event and set the handler method to be the _onCorrect() method:
     
//Marker 5: Create a event listener for "correct" bubbled up from the SWF if response is correct this.addEventListener( "correct", _onCorrect );
  1. After the "//Marker 6:" comment, create a new local SerialElement variable named serialElement and set it equal to a new SerialElement object:
//Marker 6: Construct the SerialElement adding each media element in the desired order var serialElement:SerialElement = new SerialElement();
  1. Add each of the three MediaElement objects you have created (preRoll, element and quiz) to the SerialElement by calling the addChild() method and passing it the MediaElement to add:
//Marker 6: Construct the SerialElement adding each media element in the desired order var serialElement:SerialElement = new SerialElement(); serialElement.addChild( preRoll ); serialElement.addChild( element ); serialElement.addChild( quiz );
  1. Under the "//Marker 7:" comment, create a local LayoutMetadata variable named layoutData. Set the following properties and values:
    • scaleMode: ScaleMode.LETTERBOX
    • width: 800
    • height: 600
//Marker 7: Create a LayoutMetaData object to set the unified display parameters for the serial composition and apply it to the metadata var layoutData:LayoutMetadata = new LayoutMetadata(); layoutData.scaleMode = ScaleMode.LETTERBOX; layoutData.width = 800; layoutData.height = 600;
Note: When using composition elements such as the SerialElement, the LayoutMetaData can be set directly to the composition element to achieve consistent display of its children.
 
  1. The completed initPlayer() method code should look like the following:
protected function initPlayer():void { // Create a MediaFactory instance mediaFactory = new DefaultMediaFactory(); //Marker 1: create a video element for the pre-roll video - use the newly added LOGO_VID const var preRoll:MediaElement = mediaFactory.createMediaElement( new URLResource( LOGO_VID ) ); //Marker 2: Create a StreamingURLResource to create a sub clip of the streaming resource - play from 0-20 sec var resourceStart:StreamingURLResource = new StreamingURLResource( DYNAMIC_STREAMING, StreamType.RECORDED, 0, 20 ); //Marker 3: Create the media element from the streaming resource sub clip var element:MediaElement = mediaFactory.createMediaElement( resourceStart ); //Marker 4: Create another post roll clip that is an interactive SWF piece. var quiz:MediaElement = mediaFactory.createMediaElement( new URLResource( QUIZ_SWF ) ); //Marker 5: Create a event listener for "correct" bubbled up from the SWF if response is correct this.addEventListener( "correct", _onCorrect ); //Marker 6: Construct the SerialElement adding each media element in the desired order var serialElement:SerialElement = new SerialElement(); serialElement.addChild( preRoll ); serialElement.addChild( element ); serialElement.addChild( quiz ); //Marker 7: Create a LayoutMetaData object to set the unified display parameters for the serial composition and apply it to the metadata var layoutData:LayoutMetadata = new LayoutMetadata(); layoutData.scaleMode = ScaleMode.LETTERBOX; layoutData.width = 800; layoutData.height = 600; serialElement.metadata.addValue( LayoutMetadata.LAYOUT_NAMESPACE, layoutData ); //the simplified api controller for media player = new MediaPlayer( serialElement ); //the container (sprite) for managing display and layout container = new MediaContainer(); container.addMediaElement( serialElement ); //Adds the container to the stage this.addChild( container ); }
  1. Locate the comment that begins "//Marker 8:" in the _onCorrect() event handler method. Under the comment, create a new local MediaElement variable and set it equal to a new StreamingURLResource object that creates a sub-clip of a DYNAMIC_STREAMING resource that plays from 15 seconds into the clip to the end:
//Marker 8: Note the below - and that if correct answer was triggered full play is accessed var element:MediaElement = mediaFactory.createMediaElement( new StreamingURLResource( DYNAMIC_STREAMING, StreamType.RECORDED, 15 ) );
  1. Set the media property of the player object equal to the element variable:
//Marker 8: Note the below - and that if correct answer was triggered full play is accessed var element:MediaElement = mediaFactory.createMediaElement( new StreamingURLResource( DYNAMIC_STREAMING, StreamType.RECORDED, 15 ) ); player.media = element;
  1. Call the addMediaElement() method on the container object, passing it the element variable:
//Marker 8: If correct answer was triggered full play is accessed var element:MediaElement = mediaFactory.createMediaElement( new StreamingURLResource( DYNAMIC_STREAMING, StreamType.RECORDED, 15 ) ); player.media = element; container.addMediaElement( element );
  1. Save the file and run the application. The player should play the OSMF logo clip (see Figure 5), then 20 seconds of the media clip (see Figure 6), and then present a form that asks for the title of the movie (see Figure 7). If the title is entered correctly ("elephants dream"), the rest of the clip is played (see Figure 8).
Figure 5. Video pre-roll
Figure 5. Video pre-roll
Figure 6. Main video after 20 seconds
Figure 6. Main video after 20 seconds
Figure 7. Sequenced quiz
Figure 7. Sequenced quiz
Figure 8. Playback resumed
Figure 8. Playback resumed
This walkthrough taught you how to create interactive media playback experiences by utilizing basic SerialElements to create sequential display of MediaElements. This is just the start, and can be extended in any number of ways with any type of supported media.
 
The next walkthrough will show you how to create parallel compositions that can be used for picture-in-picture, picture-by-picture, or other forms of playing two clips simultaneously.
 

 
Walkthrough 6: Basic parallel element composition

This walkthrough increases your knowledge of composition elements further with the use of the ParallelElement class to display two MediaElement instances at the same time. A key area of focus when displaying multiple elements simultaneously is layout. You can manage this task easily, utilizing the versatile OSMF LayoutContainer class and appropriate metadata. The goal of this walkthrough is to play two media items generally in sync and evenly displayed within the constraints of the LayoutContainer no matter what the LayoutContainer's size.
 
 
Objectives
  • Understand the creation and use of the ParallelElement for simultaneous media display.
  • Gain a better understanding of how to control the layout with metadata for composite elements.
  • Utilize advanced layout metadata capabilities such as percent-based layout constraints and vertical/Horizontal layoutMode rendering and alignment
 
Setup
  1. Open the file WT06_BasicParallelComposition.as in the {SAMPLES_PROJECT}/src directory.
    Note: This file has been provided as a starting point for these walkthroughs.
     
  2. Set the class file as the application file to compile. There are two different ways of doing this, depending on which program you using to build your application:
    Flash Builder: Right-click WT06_BasicParallelComposition.as and select Set as Default Application from the context menu that appears. This will add the project to the list of compilable applications. A blue dot on the file icon indicates that the file is the default application file.
     
    Flash Professional: Open OSMF_SampleTemplate.fla and save it as WT06_BasicParallelComposition.fla. Then change the document class for the file (in the Properties panel) to WT06_BasicParallelComposition.
     
 
Work with parallel elements
  1. Locate the comment that begins "//Marker 1:" in the initPlayer() method. Under the comment, create a LayoutMetadata variable named layoutData that sets the percentWidth to 50, the percentHeight to 50 and the scaleMode to LETTERBOX. This will provide a reusable LayoutMetadata object that can be applied to both media items that you want to display in unison and make them of equal scale:
//Marker 1: Create a LayoutMetaData object to even out the 2 parallel streams initially var layoutData:LayoutMetadata = new LayoutMetadata(); layoutData.percentWidth = 50; layoutData.percentHeight = 50; layoutData.scaleMode = ScaleMode.LETTERBOX;
  1. Under the "//Marker 2:" comment, create a MediaElement variable named leftElement using the static LOGO_VID constant.
  2. Add the layoutData to this element by calling the addValue() method on the metadata property of the leftElement object:
//Marker 2: Create the left side Media Element to play the LOGO_VID and apply the meta-data var leftElement:MediaElement = mediaFactory.createMediaElement( new URLResource( LOGO_VID ) ); leftElement.metadata.addValue( LayoutMetadata.LAYOUT_NAMESPACE, layoutData );
  1. Under the "//Marker 3:" comment, create a MediaElement variable named rightElement using the static STREAMING_PATH constant and apply the layoutData to this element as well:
//Marker 3: Create the right side Media Element to play the STREAMING_PATH and apply the meta-data var rightElement:MediaElement = mediaFactory.createMediaElement( new URLResource( STREAMING_PATH ) ); rightElement.metadata.addValue( LayoutMetadata.LAYOUT_NAMESPACE, layoutData );
  1. After the "//Marker 4:" comment, create a ParallelElement variable named parallelElement. And add the leftElement and rightElement as children:
//Marker 4: Create the ParallelElement and add the left and right elements to it var parallelElement:ParallelElement = new ParallelElement(); parallelElement.addChild( leftElement ); parallelElement.addChild( rightElement );
  1. Reuse the layoutData variable to create a new LayoutMetadata object that you will apply to the parallelElement object.
  2. Set the following properties and values on the layoutData object:
    • layoutMode: LayoutMode.HORIZONTAL
    • horizontalAlign: HorizontalAlign.CENTER
    • verticalAlign: VerticalAlign.MIDDLE
    • width: 800
    • height: 600
    Note: Setting the layoutMode to LayoutMode.HORIZONTAL or LayoutMode.VERTICAL can be very powerful for dynamically managing the layout of MediaElement instances within a LayoutContainer. It dynamically sets their positions appropriately based on the elements displayed and individual constraints. This is very similar to the Flex framework layout capabilities.
     
  3. Add the layoutData to the metadata property of the parallelElement object:
//Marker 5: Re-instantiate the layoutData to clear it out and set the layout data for the parallel element layoutData = new LayoutMetadata(); layoutData.layoutMode = LayoutMode.HORIZONTAL; layoutData.horizontalAlign = HorizontalAlign.CENTER; layoutData.verticalAlign = VerticalAlign.MIDDLE; layoutData.width = 800; layoutData.height = 600; parallelElement.metadata.addValue( LayoutMetadata.LAYOUT_NAMESPACE, layoutData ); The completed code should look like the following: protected function initPlayer():void { // Create a MediaFactory instance mediaFactory = new DefaultMediaFactory(); //Marker 1: Create a LayoutMetaData object up to even out the 2 parallel streams initially var layoutData:LayoutMetadata = new LayoutMetadata(); layoutData.percentWidth = 50; layoutData.percentHeight = 50; layoutData.scaleMode = ScaleMode.LETTERBOX; //Marker 2: Create the left side Media Element to play the LOGO_VID and apply the meta-data var leftElement:MediaElement = mediaFactory.createMediaElement( new URLResource( LOGO_VID ) ); leftElement.metadata.addValue( LayoutMetadata.LAYOUT_NAMESPACE, layoutData ); //Marker 3: Create the right side Media Element to play the STREAMING_PATH and apply the meta-data var rightElement:MediaElement = mediaFactory.createMediaElement( new URLResource( STREAMING_PATH ) ); rightElement.metadata.addValue( LayoutMetadata.LAYOUT_NAMESPACE, layoutData ); //Marker 4: Create the ParallelElement and add the left and right elements to it var parallelElement:ParallelElement = new ParallelElement(); parallelElement.addChild( leftElement ); parallelElement.addChild( rightElement ); //Marker 5: Re-instantiate the layoutData to clear it out and set the layout data for the parallel element layoutData = new LayoutMetadata(); layoutData.layoutMode = LayoutMode.HORIZONTAL; layoutData.horizontalAlign = HorizontalAlign.CENTER; layoutData.verticalAlign = VerticalAlign.MIDDLE; layoutData.width = 800; layoutData.height = 600; parallelElement.metadata.addValue( LayoutMetadata.LAYOUT_NAMESPACE, layoutData ); //the simplified api controller for media player = new MediaPlayer( parallelElement ); //the container (sprite) for managing display and layout container = new MediaContainer(); container.addMediaElement( parallelElement ); //Adds the container to the stage this.addChild( container ); }
  1. Save the file and run the application. Both media clips should play side-by-side (see Figure 9).
Figure 9. Parallel media clips playing side-by-side
Figure 9. Parallel media clips playing side-by-side
With a basic understanding of layout control and media compositions, it is time to take those concepts to the next level. By combining them and understanding how to use multiple layout containers in an advanced layout perspective, you as the developer will be empowered to make powerful real-world media delivery applications that can support synchronized advertisement and sequential playback with the power of merging and stacking parallel and serial elements with complex layout control.
 

 
Walkthrough 7: Working with container layouts

The purpose of this walkthrough is to give you a solid understanding of real-world applications of composition-based media elements and how to display them via the OSMF layout features. This walkthrough builds on top of the previous examples and your knowledge of SerialElement and ParallelElement, as well as basic layout metadata. Figure 10 shows the layout that this example uses. It is achieved by using multiple LayoutContainers that are dynamically positioned by a primary linked LayoutContainer object, and adding the targeted containers as layout targets of the primary container's layoutRenderer—for example:
 
mainContainer.layoutRenderer.addTarget( targetContainer )
Figure 10. Media playback layout
Figure 10. Media playback layout
Once a container has been added as a target for another container that will manage its position, the metadata and basic layout properties of the target container will be used by the managing container to determine its size and/or position.
 
Figure 11 illustrates the sequence and general structure of the complex composition this example will create.
 
Figure 11. Complex serial and parallel composition example graph
Figure 11. Complex serial and parallel composition example graph
The base of the composition is a SerialElement. It starts with a simple pre-roll video; the second element of the composition is a ParallelElement, thus playing multiple MediaElements at the same time after the pre-roll. The parallel elements consist of another SerialElement for the top banners, which display three images over a period of time: the main content, which will run for about five minutes; and a right-side info area, which is another SerialElement, and contains a video and a SWF file.
 
An important item of note is that, when displaying images, they do not have a set duration by default and will appear until manually changed. This can make it difficult to use them in a SerialElement, for instance, since the SerialElement will not know when to advance. A simple solution is to wrap the ImageElement into a DurationElement. The DurationElement is a useful class for taking an existing MediaElement and applying a manual duration to it. The DurationElement constructor accepts a defined duration, in seconds, and a MediaElement to which to apply it.
 
 
Objectives
  • Create a real-world style advertising layout.
    Compose complex media experience with nested serial and parallel compositions.
  • Understand the use of the DurationElement.
  • Use container properties and layout metadata to manage the display of the containers relative to each other.
  • Understand the basics of layout renderer's and layout targets.
 
Setup
  1. Open the file WT07_ContainerLayout.as in the {SAMPLES_PROJECT}/src directory.
    Note: This file has been provided as a starting point for these walkthroughs.
     
  2. Set the class file as the application file to compile. There are two different ways of doing this, depending on which program you are using to build your application:
    Flash Builder: Right-click WT07_ContainerLayout.as and select Set as Default Application from the context menu that appears. This will add the project to the list of compilable applications. A blue dot on the file icon indicates that the file is the default application file.
     
    Flash Professional: Open OSMF_SampleTemplate.fla and save it as WT07_ContainerLayout.fla. Then change the document class for the file (in the Properties panel) to WT07_ContainerLayout.
     
 
Create the main composition
First, create the main SerialElement that will contain a pre-roll clip. Then, play a ParallelElement that will have the main content and the advertising/info serial clips in unison:
 
  1. Locate the comment that begins "//Marker 1:" in the initPlayer() method. Under the comment, create a local SerialElement variable named rootElement and set it equal to a new SerialElement object:
//Marker 1: The root element var rootElement:SerialElement = new SerialElement();
  1. Create a local variable named preRollElement of type MediaElement and set it equal to a generated MediaElement by using the mediaFactory instance and the static const PRE_ROLL already defined:
var preRollElement:MediaElement = mediaFactory.createMediaElement( new URLResource( PRE_ROLL ) );
  1. Create a local variable called parallelComp of type ParallelComp and instantiate it accordingly:
var parallelComp:ParallelElement = new ParallelElement();
  1. Add both the preRollElement and the parallelComp to the rootElement to create a sequence of the pre-roll and the parallel composition:
rootElement.addChild( preRollElement ); rootElement.addChild( parallelComp );
 
Create the parallel composition
  1. Under the comment that begins "//Marker 2:", create a new MediaElement named mainElement and use the mediaFactory to create a new MediaElement with the static const STREAMING_MP4_PATH variable.
  2. Add the mainElement as a child of the rootElement:
//Marker 2: Main video element var mainElement:MediaElement = mediaFactory.createMediaElement( new URLResource( STREAMING_MP4_PATH ) ); rootElement.addChild( mainElement );
  1. Under the comment that begins "//Marker 3:" create a new SerialElement named topBannerElement:
//Marker 3: Top banner - serial element consisting of multiple images var topBannerElement:SerialElement = new SerialElement();
  1. Create two DurationElements named imageBanner1 and imageBanner2 using the static constant variables TOP_BANNER_1 and TOP_BANNER_2. DurationElements allow you to specify a time for a MediaElement that by default may not have a duration, such as an image:
//Marker 3: Top banner - serial element consisting of multiple images var topBannerElement:SerialElement = new SerialElement(); var imageBanner1:DurationElement = new DurationElement( BANNER_DURATION, mediaFactory.createMediaElement( new URLResource( TOP_BANNER_1 ) ) ); var imageBanner2:DurationElement = new DurationElement( BANNER_DURATION, mediaFactory.createMediaElement( new URLResource( TOP_BANNER_2 ) ) );
  1. Create a new MediaElement variable named imageBanner3 using the static const TOP_BANNER_3:
var imageBanner2:DurationElement = new DurationElement( BANNER_DURATION, mediaFactory.createMediaElement( new URLResource( TOP_BANNER_2 ) ) ); var imageBanner3:MediaElement = mediaFactory.createMediaElement( new URLResource( TOP_BANNER_3 ) );
  1. Add each imageBanner variable as a child of the topBannerElement:
var imageBanner3:MediaElement = mediaFactory.createMediaElement( new URLResource( TOP_BANNER_3 ) ); topBannerElement.addChild( imageBanner1 ); topBannerElement.addChild( imageBanner2 ); topBannerElement.addChild( imageBanner3 );
  1. Add the topBannerElement as a child of the parellelComp:
topBannerElement.addChild( imageBanner1 ); topBannerElement.addChild( imageBanner2 ); topBannerElement.addChild( imageBanner3 ); parellelComp.addChild( topBannerElement );
  1. Under the comment that begins "//Marker 4:", create a SerialElement named rightBannerElement:
//Marker 4: Right banner element var rightBannerElement:SerialElement = new SerialElement();
  1. Add two children directly to the rightBannerElement using the mediaFactory and the static const variables RIGHT_BANNER_1 and RIGHT_BANNER_2:
var rightBannerElement:SerialElement = new SerialElement(); rightBannerElement.addChild( mediaFactory.createMediaElement( new URLResource( RIGHT_BANNER_1 ) ) ); rightBannerElement.addChild( mediaFactory.createMediaElement( new URLResource( RIGHT_BANNER_2 ) ) );
  1. Add the rightBannerElement as a child of parellelComp:
rightBannerElement.addChild( mediaFactory.createMediaElement( new URLResource( RIGHT_BANNER_1 ) ) ); rightBannerElement.addChild( mediaFactory.createMediaElement( new URLResource( RIGHT_BANNER_2 ) ) ); parellelComp.addChild( rightBannerElement );
 
Create the containers
Working with containers and the advanced layout capabilities can be tricky. Generally speaking, it is best to apply the primary layout constraints to the container and let them automatically handle the layout of the elements. You can also apply layout metadata to the elements, but you must take into consideration the constraints of each element's parent container. By using both the direct properties of the MediaContainer and the properties available on the layoutMetadata of the MediaContainer, both positioning of the child elements of the containers and the relative positioning of the containers to other layout targets or containers can easily be achieved:
 
  1. Under the comment that begins "//Marker 5:", create a MediaContainer variable named rootContainer:
//Marker 5: Root container var rootContainer:MediaContainer = new MediaContainer();
  1. Set the following properties and values on the rootContainer object:
    • width: 960
    • height: 488
  2. Add the rootContainer as a child of the class:
var rootContainer:MediaContainer = new MediaContainer(); rootContainer.width = 960; rootContainer.height = 488; addChild( rootContainer );
  1. Under the comment that begins "//Marker 6:", create a new MediaContainer named topContainer; then set the following properties and values on the topContainer object:
    • backgroundColor: 0x000000
    • backgroundAlpha: 1
  2. Set the percentWidth property of the topContainer's layoutMetadata property to 100.
  3. Set the height property of the topContainer's layoutMetadata property to 60.
  4. Add the topContainer as a child of the class:
//Marker 6: Top container var topContainer:MediaContainer = new MediaContainer(); topContainer.backgroundColor = 0x666666; topContainer.backgroundAlpha = .2; topContainer.layoutMetadata.percentWidth = 100; topContainer.layoutMetadata.height = 60; addChild( topContainer );
  1. Under the comment that begins "//Marker 7:", create a new MediaContainer named rightContainer; then set the following properties and values on the rightContainer object:
    • width: 192
    • height: 428
    • backgroundColor: 0x000000
    • backgroundAlpha: 1
  2. Set the horizontalAlign property of the rightContainer's layoutMetadata property to HorizontalAlign.RIGHT:
  3. Set the verticalAlign property of the rightContainer's layoutMetadata property to VerticalAlign.BOTTOM.
  4. Add the rightContainer as a child of the class:
//Marker 7: Right container var rightContainer:MediaContainer = new MediaContainer(); rightContainer.width = 192; rightContainer.height = 428; rightContainer.backgroundColor = 0x000000; rightContainer.backgroundAlpha = 1; rightContainer.layoutMetadata.horizontalAlign = HorizontalAlign.RIGHT; rightContainer.layoutMetadata.verticalAlign = VerticalAlign.BOTTOM; addChild( rightContainer );
  1. Under the comment that begins "//Marker 8:", create a new MediaContainer named mainContainer:
  2. Set the following properties and values on the mainContainer object:
    • backgroundColor: 0x999999
    • backgroundAlpha: 1
    • width: 768
    • height: 428
    • layoutMetadata.verticalAlign: VerticalAlign.BOTTOM
  3. Add the mainContainer as a child of the class:
//Marker 8: Main content container var mainContainer:MediaContainer = new MediaContainer(); mainContainer.backgroundColor = 0x999999; mainContainer.backgroundAlpha = 1; mainContainer.width = 768; mainContainer.height = 428; mainContainer.layoutMetadata.verticalAlign = VerticalAlign.BOTTOM; addChild( mainContainer );
 
Add the MediaElements and layout targets
In this section, the media elements will be linked with their respective containers so that they are directed where to appear properly, and the containers will be linked to a single master container that manage their relative layouts based on the layout data applied earlier:
 
  1. Under the comment that begins "//Marker 9:", add the rootElement object as a MediaElement of the mainContainer by calling the addMediaElement() method of the mainContainer, passing the rootElement as the only parameter:
//Marker 9: Give the containers their media elements mainContainer.addMediaElement( rootElement );
Note: This is a critical step. This defines which content is displayed in which container.
 
  1. Add the topBannerElement object as a MediaElement of the topContainer:
  2. Add the rightBannerElement object as a MediaElement of the rightContainer:
//Marker 9: Give the containers their media elements mainContainer.addMediaElement( rootElement ); topContainer.addMediaElement( topBannerElement ); rightContainer.addMediaElement( rightBannerElement );
  1. Under the comment that begins "//Marker 10:", call the addtarget() method on the layoutRenderer property of the rootContainer, passing it the rightContainer object to add it as a layout target of the rootContainer. This links the content containers to the root container and allows the root container to dynamically position the content containers relative to each other based on their layout data:
//Marker 10: Set the layout targets for the rootContainer rootContainer.layoutRenderer.addTarget( rightContainer );
  1. Add the topContainer as a layout target of the rootContainer.
  2. Add the mainContainer as a layout target of the rootContainer:
//Marker 10: Set the layout targets for the rootContainer rootContainer.layoutRenderer.addTarget( rightContainer ); rootContainer.layoutRenderer.addTarget( topContainer ); rootContainer.layoutRenderer.addTarget( mainContainer );
  1. Save the file and run the application. You should see the player layout with three main areas: a top bar with banner images, a right bar with a video, and a SWF file and the main video display area (see Figure 12).
    The top bar uses a SerialElement to cycle through three images (see Figure 13). The first two were assigned a duration, and the last should show for the remaining time of the video.
     
    The right bar displays the OSMF logo video (see Figure 14). When the logo video is completed, a SWF file is loaded (see Figure 15).
     
Figure 12. Advanced layout pre-roll
Figure 12. Advanced layout pre-roll
Figure 13. Top banner 1, side content 1, main content
Figure 13. Top banner 1, side content 1, main content
Figure 14. Top banner 2, side content 2, main content
Figure 14. Top banner 2, side content 2, main content
Figure 15. Top banner 3, side content 2, main content
Figure 15. Top banner 3, side content 2, main content

 
Where to go from here

This second part of the series has provided a good step into the complexities and capabilities of the Open Source Media Framework. The powerful layout control and complex media compositions that OSMF has to offer speak highly to its value and depth. Gaining an understanding and taking full control of the layout and composition capabilities in OSMF is a critical step in mastering OSMF and creating custom solutions with it. The final article in this series focuses on the extensibility of OSMF using plug-ins.
 
Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License+Adobe Commercial Rights
 
This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License. Permissions beyond the scope of this license, pertaining to the examples of code included within this work are available at Adobe.