Code Sample: Creating a Custom Chromeless Video Player

This topic demonstrates how to create a custom, scaleable Chromeless Video Player template using only BEML. It also touches on several other BEML enhancements added in the Brightcove 4.1 release in April 2010.

Note: The steps outlined in this topic are for Flash based players and will not work in HTML5 players and may prevent HTML5 players from functioning.

Before getting started you may want to read Customizing Players with BEML for an overview of BEML and a list of related help topics.

In the course of this article, we will create and examine 5 variations of the custom Chromeless Video Player template.

The Basic Chromeless Video Player with Custom Controls

First, take a look at the player you'll be able to create using this template. It's displayed here at 480x270, the same dimensions as the standard Chromeless Video Player. Because it's chromeless, the controls are hidden until you mouse over the video display area. Notice that in this player, the playback controls are along the top and the playlist is along the bottom. In the standard Chromeless Video Player, there is no playlist support and the controls are along the bottom.

Template for the Basic Version

Here's the BEML template that creates all the major elements and basic functionality of this player.

<Runtime>
  <Theme name="Deluxe" style="Light"/>
  <Layout>
    <ChromelessVideoPlayer id="videoPlayer" useOverlayMenu="false"
                           video="{videoList.selectedItem}">
      <ChromelessControls boxType="vbox" visible="{!videoPlayer.menu.open}">
        <!-- Playback controls -->
        <HBox hAlign="center" height="60">
          <HBox id="defaultView" gutter="6" padding="10" vAlign="middle" maxWidth="820">
            <ToggleButton id="playButton" width="65" iconName="play" toggledIconName="pause"
                          tooltip="controls play tooltip" toggledTooltip="controls pause
                          tooltip" click="{videoPlayer.play()}"
                          toggledClick="{videoPlayer.pause()}"
                          toggled="{videoPlayer.playing}" enabled="{videoPlayer.video}"/>
            <Canvas height="30">
              <GraphicBlock/>
              <HBox gutter="4" vAlign="middle">
                <Spacer width="1"/>
                <Canvas width="40">
                  <Label id="positionLabel" y="-1"
                         text="{format(videoPlayer.mediaPosition, SecondsTimecodeFormatter)}"
                         vAlign="middle" hAlign="right" alpha=".75"/>
                </Canvas>
                <Playhead id="playhead" height="6" mediaController="{videoPlayer}"
                          useTimeToolTip="true"
                          enabled="{videoPlayer.video}"/>
                <Canvas width="40">
                  <Label id="durationLabel" y="-1"
                         text="{format(videoPlayer.mediaDuration, SecondsTimecodeFormatter)}"
                         vAlign="middle" hAlign="left" alpha=".75"/>
                </Canvas>
                <Spacer width="1"/>
              </HBox>
            </Canvas>
            <Button id="shareButton" width="35" height="30" iconName="socialShare"
                    tooltip="community socialShare tooltip" data="{videoPlayer.video}"
                    click="{videoPlayer.toggleMenuPage('Share', data)}"
                    visible="{adContext.menuOptionsVisible}"
                    includeInLayout="{!videoPlayer.fullscreen}"/>
            <ToggleButton id="fullscreenButton" width="35" height="30" iconName="maximize"
                          toggledIconName="minimize" tooltip="controls maximize tooltip"
                          toggledTooltip="controls minimize tooltip"
                          click="{videoPlayer.goFullScreen(true)}"
                          toggledClick="{videoPlayer.goFullScreen(false)}"
                          toggled="{videoPlayer.fullscreen}"/>
            <VolumeControl id="volumeButton" width="35" height="30"
                           mediaController="{videoPlayer}"
                           useOverlayLayer="false" iconName="volume" mutedIconName="muted"
                           tooltip="mute tooltip" mutedTooltip="unmute tooltip"
                           useZeroWidth="true" horizontalPadding="10" popupGutter="3"/>
          </HBox>
        </HBox>
        <Spacer height="20" includeInLayout="{videoPlayer.fullscreen}"/>
        <Spacer includeInLayout="{!videoPlayer.fullscreen}"/>
        <!-- Playlist controls -->
        <Canvas height="120" padding="10" includeInLayout="{!videoPlayer.fullscreen}">
          <GraphicBlock/>
          <VBox height="100" padding="10" vAlign="middle">
            <Spacer height="10"/>
            <TileList id="videoList" height="100" selectOnClick="true" automaticAdvance="true"
                      columnWidth="80" rowHeight="100" columnGutter="3" buttonSize="26"
                      buttonOffsetY="-5">
              <ListItem showBack="false" boxType="vbox">
                <ThumbnailButton height="60" imageInset="3" data="{currentItem}"
                                 source="{currentItem.thumbnailURL}"/>
                <Spacer height="4"/>
                <TitleLabel text="{currentItem.displayName}" size="10"
                            truncate="true" multiline="true"/>
              </ListItem>
            </TileList>
          </VBox>
        </Canvas>
      </ChromelessControls>
    </ChromelessVideoPlayer>
  </Layout>
</Runtime>

The BEML contains two main areas defining the playback controls and the playlist controls. It also leverages some key new elements, attributes, and concepts:

ChromelessControls. A new layout box that overlays the video and is only shown on rollover. You can include a ChromelessControls element in a ChromelessVideoPlayer. It serves as a container for all the interactive buttons and controls available in our custom player.

GraphicBlock. A new non-interactive graphic that can be used as skin behind other elements in a ChromelessVideoPlayer. It's used here to provide some contrast between the video and the controls.

includeInLayout. A new Boolean attribute on all UI components that specifies whether the component will be rendered and included in position and size calculations. In this template, we use it to differentiate between what appears in fullscreen and default views.

menu. A new reference to the player menu, used primarily to bind to menu.open, which returns true or false based on the menu state. This is available on the VideoPlayer, VideoDisplay, and ChromelessVideoPlayer components; we use it here to hide the ChromelessControls if the player menu is opened.

useOverlayMenu. A new Boolean attribute on the ChromelessVideoPlayer element that specifies whether or not to show the standard overlay menu. It's set to false in our example so the playback controls can be placed at the top.

useTimeToolTip. A new Boolean attribute for the Playhead element determines whether, on rollover, to display a tooltip with the current time of the mouse position.

! Operator. In BEML, you can now use the ! negation operator to invert the value of bindable Boolean properties.

Adding support for Advertising

While this template could be used as is, it does not provide any special handling for advertising. Fortunately, the BEML template can be easily modified to produce something like this.

Here's the updated BEML template:

<Runtime>
  <Theme name="Deluxe" style="Light"/>
  <Layout>
    <ChromelessVideoPlayer id="videoPlayer" useOverlayMenu="false"
                           video="{videoList.selectedItem}">
      <ChromelessControls boxType="vbox" visible="{!videoPlayer.menu.open}">
        <!-- Playback controls -->
        <HBox hAlign="center" height="60">
          <HBox id="defaultView" gutter="6" padding="10" vAlign="middle" maxWidth="820">
            <ToggleButton id="playButton" width="65" iconName="play" toggledIconName="pause"
                          tooltip="controls play tooltip" toggledTooltip="controls pause tooltip"
                          click="{videoPlayer.play()}" toggledClick="{videoPlayer.pause()}"
                          toggled="{videoPlayer.playing}" enabled="{videoPlayer.video}"
                          includeInLayout="{adContext.playPauseVisible}"/>
            <Canvas height="30">
              <GraphicBlock/>
              <!-- Non-Ad mode -->
              <HBox gutter="4" vAlign="middle" visible="{!adContext.sponsorMessageVisible}">
                <Spacer width="1"/>
                <Canvas width="40">
                  <Label id="positionLabel" y="-1"
                         text="{format(videoPlayer.mediaPosition, SecondsTimecodeFormatter)}"
                         vAlign="middle" hAlign="right" alpha=".75"/>
                </Canvas>
                <Playhead id="playhead" height="6" mediaController="{videoPlayer}"
                          useTimeToolTip="true" enabled="{videoPlayer.video}"/>
                <Canvas width="40">
                  <Label id="durationLabel" y="-1"
                         text="{format(videoPlayer.mediaDuration, SecondsTimecodeFormatter)}"
                         vAlign="middle" hAlign="left" alpha=".75"/>
                </Canvas>
                <Spacer width="1"/>
              </HBox>
              <!-- Ad mode -->
              <VBox visible="{adContext.sponsorMessageVisible}">
                <HBox vAlign="middle">
                  <Spacer width="7"/>
                  <Label text="sponsor message" vAlign="middle"/>
                  <Label id="adTimePosition" visible="{adContext.timePositionVisible}"
                         hAlign="right" vAlign="middle"
                         text="{format(videoPlayer.positiveTimeRemaining, SecondsTimecodeFormatter)}"/>
                  <Spacer width="7"/>
                </HBox>
                <Spacer height="1"/>
              </VBox>
            </Canvas>
            <Button id="shareButton" width="35" height="30" iconName="socialShare"
                    tooltip="community socialShare tooltip" data="{videoPlayer.video}"
                    click="{videoPlayer.toggleMenuPage('Share', data)}"
                    visible="{adContext.menuOptionsVisible}"
                    includeInLayout="{!videoPlayer.fullscreen}"/>
            <ToggleButton id="fullscreenButton" width="35" height="30" iconName="maximize"
                    toggledIconName="minimize" tooltip="controls maximize tooltip"
                    toggledTooltip="controls minimize tooltip" click="{videoPlayer.goFullScreen(true)}"
                    toggledClick="{videoPlayer.goFullScreen(false)}" toggled="{videoPlayer.fullscreen}"
                    includeInLayout="{adContext.maximizeVisible}"/>
            <VolumeControl id="volumeButton" width="35" height="30" mediaController="{videoPlayer}"
                           useOverlayLayer="false" iconName="volume" mutedIconName="muted"
                           tooltip="mute tooltip" mutedTooltip="unmute tooltip" useZeroWidth="true"
                           horizontalPadding="10" includeInLayout="{adContext.volumeVisible}"
                           popupGutter="3"/>
          </HBox>
        </HBox>
        <Spacer height="20" includeInLayout="{videoPlayer.fullscreen}"/>
        <Spacer includeInLayout="{!videoPlayer.fullscreen}"/>
        <!-- Playlist controls -->
        <Canvas height="120" padding="10" includeInLayout="{!videoPlayer.fullscreen}"
                visible="{!adContext.sponsorMessageVisible}">
          <GraphicBlock/>
          <VBox height="100" padding="10" vAlign="middle">
            <Spacer height="10"/>
            <TileList id="videoList" height="100" selectOnClick="true" automaticAdvance="true" columnWidth="80"
                      rowHeight="100" columnGutter="3" buttonSize="26" buttonOffsetY="-5">
              <ListItem showBack="false" boxType="vbox">
                <ThumbnailButton height="60" imageInset="3" data="{currentItem}"
                                 source="{currentItem.thumbnailURL}"/>
                <Spacer height="4"/>
                <TitleLabel text="{currentItem.displayName}" size="10"
                            truncate="true" multiline="true"/>
              </ListItem>
            </TileList>
          </VBox>
        </Canvas>
      </ChromelessControls>
    </ChromelessVideoPlayer>
  </Layout>
</Runtime>

Note the use of adContext to reference the non-visual AdvertisingContext component. This allows the template to selectively show or hide controls appropriately. If you don't include the adContext parameters as shown here, ads will not work well in players built from this template. If you are not sure if you will use advertising, it's best to build in the support just in case, since players will work whether or not you have advertising enabled for the player. Read more about enabling advertising in custom player templates.

Adding Support for Different Playback Types

In addition to supporting advertising, it's now possible to handle different playback types. For example, you can display different playback controls, depending on whether the player is running a live stream, a bumper video, or standard on-demand content.

Here's the updated BEML with this support.

<Runtime>
  <Theme name="Deluxe" style="Light"/>
  <Layout>
    <ChromelessVideoPlayer id="videoPlayer" useOverlayMenu="false"
                           video="{videoList.selectedItem}">
      <ChromelessControls boxType="vbox" visible="{!videoPlayer.menu.open}">
         <!-- Playback controls -->
        <HBox hAlign="center" height="60">
          <HBox id="defaultView" gutter="6" padding="10" vAlign="middle" maxWidth="820">
            <ToggleButton id="playButton" width="65" iconName="play" toggledIconName="pause"
                          tooltip="controls play tooltip" toggledTooltip="controls pause tooltip"
                          click="{videoPlayer.play()}" toggledClick="{videoPlayer.pause()}"
                          toggled="{videoPlayer.playing}" enabled="{videoPlayer.video}"
                          includeInLayout="{adContext.playPauseVisible}"/>
            <Canvas height="30">
              <GraphicBlock/>
              <!-- Non-Ad mode -->
              <ViewStack selectedIndex="{videoPlayer.playbackType}"
                         visible="{!adContext.sponsorMessageVisible}">
                <!-- VOD -->
                <HBox gutter="4" vAlign="middle">
                  <Spacer width="1"/>
                  <Canvas width="40">
                    <Label id="positionLabel" y="-1"
                           text="{format(videoPlayer.mediaPosition, SecondsTimecodeFormatter)}"
                           vAlign="middle" hAlign="right" alpha=".75"/>
                  </Canvas>
                  <Playhead id="playhead" height="6" mediaController="{videoPlayer}"
                            useTimeToolTip="true" enabled="{videoPlayer.video}"/>
                  <Canvas width="40">
                    <Label id="durationLabel" y="-1"
                           text="{format(videoPlayer.mediaDuration, SecondsTimecodeFormatter)}"
                           vAlign="middle" hAlign="left" alpha=".75"/>
                  </Canvas>
                  <Spacer width="1"/>
                </HBox>
                <!-- Live -->
                <HBox padding="7">
                  <Canvas>
                    <Label text="live media message" y="-1" truncate="true"/>
                  </Canvas>
                </HBox>
                <!-- Bumper -->
                <HBox/>
              </ViewStack>
              <!-- Ad mode -->
              <VBox visible="{adContext.sponsorMessageVisible}">
                <HBox vAlign="middle">
                  <Spacer width="7"/>
                  <Label text="sponsor message" vAlign="middle"/>
                  <Label id="adTimePosition" visible="{adContext.timePositionVisible}"
                         hAlign="right" vAlign="middle"
                         text="{format(videoPlayer.positiveTimeRemaining, SecondsTimecodeFormatter)}"/>
                  <Spacer width="7"/>
                </HBox>
                <Spacer height="1"/>
              </VBox>
            </Canvas>
            <Button id="shareButton" width="35" height="30" iconName="socialShare"
                        tooltip="community socialShare tooltip" data="{videoPlayer.video}"
                        click="{videoPlayer.toggleMenuPage('Share', data)}"
                        visible="{adContext.menuOptionsVisible}"
                        includeInLayout="{!videoPlayer.fullscreen}"/>
            <ToggleButton id="fullscreenButton" width="35" height="30" iconName="maximize"
                          toggledIconName="minimize" tooltip="controls maximize tooltip"
                          toggledTooltip="controls minimize tooltip" click="{videoPlayer.goFullScreen(true)}"
                          toggledClick="{videoPlayer.goFullScreen(false)}" toggled="{videoPlayer.fullscreen}"
                          includeInLayout="{adContext.maximizeVisible}"/>
            <VolumeControl id="volumeButton" width="35" height="30" mediaController="{videoPlayer}"
                           useOverlayLayer="false" iconName="volume" mutedIconName="muted"
                           tooltip="mute tooltip" mutedTooltip="unmute tooltip"
                           useZeroWidth="true" horizontalPadding="10"
                           includeInLayout="{adContext.volumeVisible}" popupGutter="3"/>
          </HBox>
        </HBox>
        <Spacer height="20" includeInLayout="{videoPlayer.fullscreen}"/>
        <Spacer includeInLayout="{!videoPlayer.fullscreen}"/>
        <!-- Playlist controls -->
        <Canvas height="120" padding="10" includeInLayout="{!videoPlayer.fullscreen}"
                visible="{!adContext.sponsorMessageVisible}">
          <GraphicBlock/>
          <VBox height="100" padding="10" vAlign="middle">
            <Spacer height="10"/>
            <TileList id="videoList" height="100" selectOnClick="true"
                      automaticAdvance="true" columnWidth="80"
                      rowHeight="100" columnGutter="3" buttonSize="26" buttonOffsetY="-5">
              <ListItem showBack="false" boxType="vbox">
                <ThumbnailButton height="60" imageInset="3" data="{currentItem}"
                                 source="{currentItem.thumbnailURL}"/>
                <Spacer height="4"/>
                <TitleLabel text="{currentItem.displayName}" size="10"
                            truncate="true" multiline="true"/>
              </ListItem>
            </TileList>
          </VBox>
        </Canvas>
      </ChromelessControls>
    </ChromelessVideoPlayer>
  </Layout>
</Runtime>

Note the non-advertising playback control area now has handling for different cases. There are two new important concepts being introduced here.

ViewStack. A new layout box that only makes a single child visible at a time, as specified by the selectedIndex attribute. In this case, there are three children defined. These correspond to the possible values of the new playbackType attribute.

playbackType. A new bindable property on VideoPlayer, VideoDisplay, and ChromelessVideoPlayer that indicates the current playback type. Possible values are:

  • 0 = On-demand content
  • 1 = Live stream
  • 2 = Bumper video
  • 3 = Live with DVR 

This is especially useful to enable you to display different player controls, or hide them altogether, based on whether the player is showing a regular on-demand video, a live stream (since the viewer can't scrub ahead into the future), a live stream with DVR capability (a button can enable the viewer to pause a live stream and catch up later) or a bumper.

Adding a Compact Mode

To make this template scale better, the BEML can be expanded to define a special layout for smaller sizes. Here's a player at 320x180 using this template.

Here's the final version of our BEML template, which uses different custom controls, depending on both the player size (a more compact set of controls is used if the player is 320 or fewer pixels wide) and playback type (the playhead is replaced with a message if the playback type is live video, and blank during playback of a bumper):

<Runtime>
  <Theme name="Deluxe" style="Light"/>
  <Layout>
    <ChromelessVideoPlayer id="videoPlayer" useOverlayMenu="false" compactViewThreshold="320"
                           video="{videoList.selectedItem}">
      <ChromelessControls boxType="vbox" visible="{!videoPlayer.menu.open}">
        <!-- Playback controls -->
        <HBox hAlign="center" height="60">
          <ViewStack id="viewStack" maxWidth="820">
            <!-- Standard view (>320px wide) -->
            <HBox id="defaultView" gutter="6" padding="10" vAlign="middle">
              <ToggleButton id="playButton" width="65" iconName="play" toggledIconName="pause"
                            tooltip="controls play tooltip" toggledTooltip="controls pause tooltip"
                            click="{videoPlayer.play()}" toggledClick="{videoPlayer.pause()}"
                            toggled="{videoPlayer.playing}" enabled="{videoPlayer.video}"
                            includeInLayout="{adContext.playPauseVisible}"/>
              <Canvas height="30">
                <GraphicBlock/>
                <!-- Non-Ad mode -->
                <ViewStack selectedIndex="{videoPlayer.playbackType}"
                           visible="{!adContext.sponsorMessageVisible}">
                  <!-- VOD -->
                  <HBox gutter="4" vAlign="middle">
                    <Spacer width="1"/>
                    <Canvas width="40">
                      <Label id="positionLabel" y="-1"
                             text="{format(videoPlayer.mediaPosition, SecondsTimecodeFormatter)}"
                             vAlign="middle" hAlign="right" alpha=".75"/>
                    </Canvas>
                    <Playhead id="playhead" height="6" mediaController="{videoPlayer}"
                              useTimeToolTip="true" enabled="{videoPlayer.video}"/>
                    <Canvas width="40">
                      <Label id="durationLabel" y="-1" text="{format(videoPlayer.mediaDuration, SecondsTimecodeFormatter)}"
                             vAlign="middle" hAlign="left" alpha=".75"/>
                    </Canvas>
                    <Spacer width="1"/>
                  </HBox>
                  <!-- Live -->
                  <HBox padding="7">
                    <Canvas>
                      <Label text="live media message" y="-1" truncate="true"/>
                    </Canvas>
                  </HBox>
                  <!-- Bumper -->
                  <HBox/>
                </ViewStack>
                <!-- Ad mode -->
                <VBox visible="{adContext.sponsorMessageVisible}">
                  <HBox vAlign="middle">
                    <Spacer width="7"/>
                    <Label text="sponsor message" vAlign="middle"/>
                    <Label id="adTimePosition" visible="{adContext.timePositionVisible}"
                           hAlign="right" vAlign="middle"
                           text="{format(videoPlayer.positiveTimeRemaining, SecondsTimecodeFormatter)}"/>
                    <Spacer width="7"/>
                  </HBox>
                  <Spacer height="1"/>
                </VBox>
              </Canvas>
              <Button id="shareButton" width="35" height="30" iconName="socialShare"
                      tooltip="community socialShare tooltip" data="{videoPlayer.video}"
                      click="{videoPlayer.toggleMenuPage('Share', data)}"
                      visible="{adContext.menuOptionsVisible}"
                      includeInLayout="{!videoPlayer.fullscreen}"/>
              <ToggleButton id="fullscreenButton" width="35" height="30" iconName="maximize"
                            toggledIconName="minimize" tooltip="controls maximize tooltip"
                            toggledTooltip="controls minimize tooltip"
                            click="{videoPlayer.goFullScreen(true)}"
                            toggledClick="{videoPlayer.goFullScreen(false)}"
                            toggled="{videoPlayer.fullscreen}"
                            includeInLayout="{adContext.maximizeVisible}"/>
              <VolumeControl id="volumeButton" width="35" height="30" mediaController="{videoPlayer}"
                             useOverlayLayer="false" iconName="volume" mutedIconName="muted"
                             tooltip="mute tooltip" mutedTooltip="unmute tooltip"
                             useZeroWidth="true" horizontalPadding="10"
                             includeInLayout="{adContext.volumeVisible}" popupGutter="3"/>
            </HBox>
            <!-- Compact view (<=320px wide) -->
            <HBox id="compactView" padding="5" vAlign="bottom">
              <HBox height="30" gutter="4">
                <ToggleButton id="compactPlayButton" width="35" iconName="play" toggledIconName="pause"
                      tooltip="controls play tooltip" toggledTooltip="controls pause tooltip"
                      click="{videoPlayer.play()}" toggledClick="{videoPlayer.pause()}"
                      toggled="{videoPlayer.playing}" enabled="{videoPlayer.video}"/>
                <Canvas>
                  <GraphicBlock/>
                  <ViewStack selectedIndex="{videoPlayer.playbackType}"
                             visible="{!adContext.sponsorMessageVisible}">
                    <!-- Normal playback (no ad) -->
                    <HBox padding="10" vAlign="middle">
                      <Playhead id="compactPlayhead" height="6" mediaController="{videoPlayer}"
                                useTimeToolTip="true" enabled="{videoPlayer.video}"/>
                    </HBox>
                    <!-- Live -->
                    <HBox padding="7">
                      <Canvas>
                        <Label text="live media message" y="-1" truncate="true"/>
                      </Canvas>
                    </HBox>
                    <!-- Bumper -->
                    <HBox/>
                  </ViewStack>
                  <VBox visible="{adContext.sponsorMessageVisible}">
                    <HBox vAlign="middle">
                      <Spacer width="7"/>
                      <Label id="compactAdTimePosition" visible="{adContext.timePositionVisible}"
                             hAlign="right" vAlign="middle"
                             text="{format(videoPlayer.positiveTimeRemaining, SecondsTimecodeFormatter)}"/>
                      <Spacer width="7"/>
                    </HBox>
                    <Spacer height="1"/>
                  </VBox>
                </Canvas>
                <Button id="compactShareButton" width="35" height="30" iconName="socialShare"
                        tooltip="community socialShare tooltip" data="{videoPlayer.video}"
                        click="{videoPlayer.toggleMenuPage('Share', data)}"
                        visible="{adContext.menuOptionsVisible}" includeInLayout="{!videoPlayer.fullscreen}"/>
                <ToggleButton id="compactFullscreenButton" width="35" height="30" iconName="maximize"
                        toggledIconName="minimize" tooltip="controls maximize tooltip"
                        toggledTooltip="controls minimize tooltip" click="{videoPlayer.goFullScreen(true)}"
                        toggledClick="{videoPlayer.goFullScreen(false)}" toggled="{videoPlayer.fullscreen}"
                        includeInLayout="{adContext.maximizeVisible}"/>
                <ToggleButton id="compactVolumeButton" width="35" height="30" iconName="volume"
                        toggledIconName="muted" toggledTooltip="Unmute" tooltip="Mute"
                        click="{videoPlayer.mute(true)}" toggledClick="{videoPlayer.mute(false)}"
                        toggled="{videoPlayer.muted}" includeInLayout="{adContext.volumeVisible}"/>
              </HBox>
            </HBox>
          </ViewStack>
        </HBox>
        <Spacer height="20" includeInLayout="{videoPlayer.fullscreen}"/>
        <Spacer includeInLayout="{!videoPlayer.fullscreen}"/>
        <!-- Playlist controls -->
        <Canvas height="120" padding="10" includeInLayout="{!videoPlayer.fullscreen}"
                visible="{!adContext.sponsorMessageVisible}">
          <GraphicBlock/>
          <VBox height="100" padding="10" vAlign="middle">
            <Spacer height="10"/>
            <TileList id="videoList" height="100" selectOnClick="true"
                      automaticAdvance="true" columnWidth="80"
                      rowHeight="100" columnGutter="3" buttonSize="26" buttonOffsetY="-5">
              <ListItem showBack="false" boxType="vbox">
                <ThumbnailButton height="60" imageInset="3" data="{currentItem}"
                                 source="{currentItem.thumbnailURL}"/>
                <Spacer height="4"/>
                <TitleLabel text="{currentItem.displayName}" size="10"
                            truncate="true" multiline="true"/>
              </ListItem>
            </TileList>
          </VBox>
        </Canvas>
      </ChromelessControls>
    </ChromelessVideoPlayer>
  </Layout>
</Runtime>

To achieve this, we introduced a new ViewStack around the playback controls. If the ChromelessVideoPlayer detects a child element of a ViewStack that has the id of "defaultView", the player checks its current width against the value of the compactViewThreshold attribute. If the width is less than or equal to the compactViewThreshold (in this example, 320 pixels), the player automatically toggles to the child element of the ViewStack that has the id of of "compactView."

Note that the contents of the "compactView" child are nearly identical to the contents of the "defaultView" child, including handling for all the various playback types and ads. The only difference is in the definition of the individual controls, where some items are smaller or altogether excluded. The VolumeControl was also swapped with a ToggleButton with simple mute/unmute functionality.

Finally, it's important to notice that the playlist control area was not modified between the two views. This is because there can only be one item with the id "videoList" which drives how content is programmed and displayed. The TileList component scales well without any special handling, so this does not create a problem for our template.

Live with DVR using the LiveButton component

Live video with DVR is another playback type with its own special behavior in the player. Live video with DVR makes a live video act like it was being played through the viewer's digital video recorder. If you are streaming a live event, DVR controls allow your viewers to scrub back in time, while the live event is still in progress, to replay a highlight or check out a clip they may have missed. The live button in the player controls allows the viewer to return to the live action after pausing or scrubbing backwards in the stream. 

The live button is automatically present when a Live DVR stream is played back in a standard player or in a custom player that uses the VideoPlayer or ChromelessVideoPlayer BEML components. You can add a live button to the player controls of a custom player template, using the LiveButton BEML component. Here's an example that shows how this works with the custom chromeless player controls we've been working with:

<Runtime>
  <Theme name="Deluxe" style="Light"/>
  <Layout>
    <ChromelessVideoPlayer id="videoPlayer" useOverlayMenu="false" video="{videoList.selectedItem}">
      <ChromelessControls boxType="vbox" visible="{!videoPlayer.menu.open}">
        <!-- Playback controls -->
        <HBox hAlign="center" height="60">
          <HBox id="defaultView" gutter="6" padding="10" vAlign="middle" maxWidth="820">
            <ToggleButton id="playButton" width="65" iconName="play"
              toggledIconName="pause" tooltip="controls play tooltip"
              toggledTooltip="controls pause tooltip" click="{videoPlayer.play()}"
              toggledClick="{videoPlayer.pause()}" toggled="{videoPlayer.playing}"
              enabled="{videoPlayer.video}" includeInLayout="{adContext.playPauseVisible}"/>
            <Canvas height="30">
              <GraphicBlock/>
              <!-- Non-Ad mode -->
              <ViewStack selectedIndex="{videoPlayer.playbackType}" visible="{!adContext.sponsorMessageVisible}">
                <!-- VOD -->
                <HBox gutter="4" vAlign="middle">
                  <Spacer width="1"/>
                  <Canvas width="40">
                    <Label id="positionLabel" y="-1" text="{format(videoPlayer.mediaPosition, SecondsTimecodeFormatter)}" vAlign="middle" hAlign="right" alpha=".75"/>
                  </Canvas>
                  <Playhead id="playhead" height="6" mediaController="{videoPlayer}" useTimeToolTip="true" enabled="{videoPlayer.video}"/>
                  <Canvas width="40">
                    <Label id="durationLabel" y="-1" text="{format(videoPlayer.mediaDuration, SecondsTimecodeFormatter)}" vAlign="middle" hAlign="left" alpha=".75"/>
                  </Canvas>
                  <Spacer width="1"/>
                </HBox>
                <!-- Live -->
                <HBox padding="7">
                  <Canvas>
                    <Label text="live media message" y="-1" truncate="true"/>
                  </Canvas>
                </HBox>
                <!-- Bumper -->
                <HBox/>
                <!-- Live DVR -->
                <HBox gutter="4" vAlign="middle">
                  <Spacer width="1"/>
                  <Canvas width="40">
                    <Label id="dvrFullPositionLabel" y="-1" text="{format(videoPlayer.mediaPosition, SecondsTimecodeFormatter)}" vAlign="middle" hAlign="right" alpha=".75"/>
                  </Canvas>
                  <Playhead id="dvrFullPlayhead" height="6" mediaController="{videoPlayer}" useTimeToolTip="true" enabled="{videoPlayer.video}"/>
                  <Spacer width="1"/>
                  <LiveButton label="live dvr message" id="liveButtonFull" labelBuffer="8" height="20" labelAlignmentV="top" autoSize="true"
                    lockHeight="true" tooltip="live dvr tooltip" enabled="{!videoPlayer.isPlayingLive}" click="{videoPlayer.seek(-1)}"/>
                  <Spacer width="1"/>
                </HBox>
              </ViewStack>
              <!-- Ad mode -->
              <VBox visible="{adContext.sponsorMessageVisible}">
                <HBox vAlign="middle">
                  <Spacer width="7"/>
                  <Label text="sponsor message" vAlign="middle"/>
                  <Label id="adTimePosition" visible="{adContext.timePositionVisible}" hAlign="right" vAlign="middle" text="{format(videoPlayer.positiveTimeRemaining, SecondsTimecodeFormatter)}"/>
                  <Spacer width="7"/>
                </HBox>
                <Spacer height="1"/>
              </VBox>
            </Canvas>
            <Button id="shareButton" width="35" height="30" iconName="socialShare" tooltip="community socialShare tooltip"
              data="{videoPlayer.video}" click="{videoPlayer.toggleMenuPage('Share', data)}" visible="{adContext.menuOptionsVisible}"
              includeInLayout="{!videoPlayer.fullscreen}"/>
            <ToggleButton id="fullscreenButton" width="35" height="30" iconName="maximize" toggledIconName="minimize"
              tooltip="controls maximize tooltip" toggledTooltip="controls minimize tooltip" click="{videoPlayer.goFullScreen(true)}"
              toggledClick="{videoPlayer.goFullScreen(false)}" toggled="{videoPlayer.fullscreen}" includeInLayout="{adContext.maximizeVisible}"/>
            <VolumeControl id="volumeButton" width="35" height="30" mediaController="{videoPlayer}" useOverlayLayer="false" iconName="volume"
              mutedIconName="muted" tooltip="mute tooltip" mutedTooltip="unmute tooltip" useZeroWidth="true" horizontalPadding="10"
              includeInLayout="{adContext.volumeVisible}" popupGutter="3"/>
          </HBox>
        </HBox>
        <Spacer height="20" includeInLayout="{videoPlayer.fullscreen}"/>
        <Spacer includeInLayout="{!videoPlayer.fullscreen}"/>
        <!-- Playlist controls -->
        <Canvas height="120" padding="10" includeInLayout="{!videoPlayer.fullscreen}" visible="{!adContext.sponsorMessageVisible}">
          <GraphicBlock/>
          <VBox height="100" padding="10" vAlign="middle">
            <Spacer height="10"/>
            <TileList id="videoList" height="100" selectOnClick="true" automaticAdvance="true" columnWidth="80" rowHeight="100" columnGutter="3" buttonSize="26" buttonOffsetY="-5">
              <ListItem showBack="false" boxType="vbox">
                <ThumbnailButton height="60" imageInset="3" data="{currentItem}" source="{currentItem.thumbnailURL}"/>
                <Spacer height="4"/>
                <TitleLabel text="{currentItem.displayName}" size="10" truncate="true" multiline="true"/>
              </ListItem>
            </TileList>
          </VBox>
        </Canvas>
      </ChromelessControls>
    </ChromelessVideoPlayer>
  </Layout>
</Runtime> 

Notice a fourth HBox has been added to the existing view stack, which includes a LiveButton element. This element represents a button that the viewer can click to quickly be taken back to live mode after scrubbing back or pausing for a long period of time. Within the LiveButton element, the enabled attribute is bound to the isPlayingLive read-only property. This simply prevents the button from taking action while the player is already in live mode. The click attribute uses the seek method with a value of -1 to indicate live. More detailed documentation on the LiveButton BEML component can be found in the BEML DTD Reference.  Read more about Delivering Live Video with DVR.

Player with controls at the bottom

When this article was originally written, the chromeless player was architected slightly differently, which is why the controls in the examples above appear at the top of the player, rather than the bottom. Here is BEML code for a custom player that places the controls at the bottom:

<Runtime>
  <Theme name="Deluxe" style="Light"/>
  <Layout>
    <ChromelessVideoPlayer id="videoPlayer" useOverlayMenu="false" video="{videoList.selectedItem}">
      <ChromelessControls boxType="hbox" visible="{!videoPlayer.menu.open}" vAlign="bottom">
        <!-- Playback controls -->
        <HBox hAlign="center" height="30" width="100%">
          <HBox id="defaultView" gutter="0" padding="0" hAlign="center" vAlign="middle" width="100%" maxWidth="100%">
            <!-- Play / Pause button -->
            <ToggleButton id="playButton" width="46" iconName="play" toggledIconName="pause" tooltip="play" toggledTooltip="pause" click="{videoPlayer.play()}" toggledClick="{videoPlayer.pause()}" toggled="{videoPlayer.playing}" enabled="{videoPlayer.video}" includeInLayout="{adContext.playPauseVisible}"/>
            <!-- Progress Bar -->
            <Canvas height="30">
              <GraphicBlock/>
              <HBox gutter="4" hAlign="center" vAlign="middle">
                <Spacer width="1"/>
                <Playhead id="playhead" height="6" width="80%" mediaController="{videoPlayer}" useTimeToolTip="true" enabled="{videoPlayer.video}"/>
                <Canvas width="40">
                  <Label id="durationLabel" y="-1" text="{format(videoPlayer.mediaDuration, SecondsTimecodeFormatter)}" vAlign="middle" hAlign="left" alpha=".75" visible="{!adContext.timePositionVisible}"/>
                </Canvas>
                <Label id="adTimePosition" visible="{adContext.timePositionVisible}" hAlign="right" vAlign="middle" text="{format(videoPlayer.positiveTimeRemaining, SecondsTimecodeFormatter)}"/>
                <Spacer width="1"/>
              </HBox>
            </Canvas>
            <Button id="shareButton" width="35" height="30" iconName="socialShare" tooltip="community socialShare tooltip" data="{videoPlayer.video}" click="{videoPlayer.toggleMenuPage('Share', data)}" visible="{adContext.menuOptionsVisible}" includeInLayout="{!videoPlayer.fullscreen}"/>
            <ToggleButton id="fullscreenButton" width="35" height="30" iconName="maximize" toggledIconName="minimize" tooltip="controls maximize tooltip" toggledTooltip="controls minimize tooltip" click="{videoPlayer.goFullScreen(true)}" toggledClick="{videoPlayer.goFullScreen(false)}" toggled="{videoPlayer.fullscreen}" includeInLayout="{adContext.maximizeVisible}"/>
            <VolumeControl id="volumeButton" width="35" height="30" mediaController="{videoPlayer}" useOverlayLayer="false" iconName="volume" mutedIconName="muted" tooltip="mute tooltip" mutedTooltip="unmute tooltip" useZeroWidth="true" horizontalPadding="10" includeInLayout="{adContext.volumeVisible}" popupGutter="3"/>
          </HBox>
        </HBox>
      </ChromelessControls>
    </ChromelessVideoPlayer>
  </Layout>
</Runtime>

And here is a player created from that code:

Post new comment

The content of this field is kept private and will not be shown publicly.
0

Comments