Double Box Ad

The Double Box Ad integration enables cinematic, high-impact advertising within the StreamLayer TV SDK. When a promotion activates, the video player smoothly shrinks to half its width and an ad panel appears beside it. The SDK manages all positioning, animation, and layout — you only need to wire up a few props on the StreamLayerSDKTv component.

Overview

The double box ad format splits the screen into a 50/50 layout — the video player animates into the left half while ad content appears on the right. The SDK handles all positioning, animation, and responsive behavior automatically.

Prerequisites

Ensure you have completed the Integration Guide before adding double box ads.


Integration

Step 1 — Install Packages

Install the StreamLayer TV SDK:

npm install @streamlayer/web-os

Step 2 — Import Styles

Import the SDK stylesheet in your application entry point. This is required — without it the double box layout, animations, and all other SDK UI will render without styles.

import '@streamlayer/web-os/style.css'

Step 3 — Set Up the Layout

Pass the doubleBox prop to StreamLayerSDKTv alongside a videoRef pointing to your video container element. When a double box ad activates, the SDK takes control of the video element's position and smoothly animates it into the left frame of the 50/50 split layout.

import { useRef } from 'react'
import { StreamLayerProvider, StreamLayerSDKTv } from '@streamlayer/web-os'
import '@streamlayer/web-os/style.css'

export const Main = () => {
  const videoContainerRef = useRef<HTMLDivElement>(null)

  return (
    <StreamLayerProvider sdkKey="your-sdk-key" event="your-event-id">
      <div id="player-root" style={{ position: 'relative' }}>
        <div ref={videoContainerRef}>
          <video src="https://example.com/stream.m3u8" autoPlay controls />
        </div>

        <StreamLayerSDKTv
          doubleBox
          videoRef={videoContainerRef}
        >
          {/* StreamLayerSDKTv manages interactive units */}
        </StreamLayerSDKTv>
      </div>
    </StreamLayerProvider>
  )
}

Important: The video's parent container must have position: relative for correct absolute positioning.


Props

PropTypeDescription
doubleBoxbooleanRequired. Enables double box ad mode.
externalAdbooleanEnables VAST/GAM external ad support. When true, the SDK renders VAST video ads.
mutedbooleanMutes the active ad video. Default: false.
videoRefReact.RefObject<HTMLElement | null>Required. Ref to the host video container for positioning.
videoPlayerControllerVideoPlayerCallbackCallback for video control events (mute, play) during ad playback.
persistentbooleanShow the ad each time it activates, ignoring the "already viewed" check.

VideoPlayerCallback

type VideoPlayerCallback = (data: { muted?: boolean; play?: boolean }) => void

Register this callback to react to SDK-initiated video control events. For example, when an external VAST ad plays, the SDK may request muting the host video.


How It Works

  1. Promotion activates — the SDK renders the ad layout off-screen to measure the target slot dimensions.
  2. Style capture — the SDK snapshots the video element's current inline styles so they can be restored later.
  3. Animated transition — the video element receives position: absolute and is smoothly transitioned (750ms) into the left half of the layout while the ad content fades in on the right.
  4. Live tracking — a ResizeObserver watches the layout slot, keeping the video coordinates up-to-date on window or container resize.
  5. Ad closes — the video transitions back to its original size and position, and the saved inline styles are restored.

Note: The SDK temporarily overrides the video element's inline styles while the ad is visible. Avoid setting the same style properties on the video element during this time.


Implementation Example

import { useCallback, useRef, useState } from 'react'
import { StreamLayerProvider, StreamLayerSDKTv, type VideoPlayerCallback } from '@streamlayer/web-os'
import '@streamlayer/web-os/style.css'

export const VideoView = () => {
  const videoRef = useRef<HTMLVideoElement>(null)
  const videoContainerRef = useRef<HTMLDivElement>(null)
  const [muted, setMuted] = useState(false)

  // Called when SDK requests video control (e.g., mute during external ad)
  const videoPlayerController: VideoPlayerCallback = useCallback((data) => {
    if (!videoRef.current) return

    if (data.muted !== undefined) {
      videoRef.current.muted = data.muted
      setMuted(data.muted)
    }
    if (data.play === true) {
      videoRef.current.play()
    }
  }, [])

  return (
    <StreamLayerProvider sdkKey="your-sdk-key" event="your-event-id">
      <div style={{ position: 'relative' }}>
        <div ref={videoContainerRef}>
          <video
            ref={videoRef}
            src="https://example.com/stream.m3u8"
            autoPlay
            controls
          />
        </div>

        <StreamLayerSDKTv
          doubleBox
          externalAd
          videoRef={videoContainerRef}
          videoPlayerController={videoPlayerController}
          muted={muted}
        >
          {/* StreamLayerSDKTv manages interactive units */}
        </StreamLayerSDKTv>
      </div>
    </StreamLayerProvider>
  )
}

Key Points

  1. videoRef is required: Pass a ref to your video container element. The video's parent must have position: relative.
  2. Enable externalAd for VAST ads: If your promotions include external VAST/GAM video ads, pass externalAd alongside doubleBox.
  3. Register videoPlayerController: Implement this callback to respond to SDK-initiated mute/play requests during ad playback.
  4. Don't fight inline styles: The SDK temporarily overrides video element styles during ad playback. Avoid modifying the same properties while an ad is active.

Muting the Host Video During Ad Playback

When a double box ad includes audio (e.g., an external VAST video ad), two audio sources play simultaneously — the host video stream and the ad. The SDK uses the videoPlayerController callback to request that the host video is muted while the ad is playing and unmuted when the ad ends.

How it works

  1. The SDK calls your videoPlayerController with { muted: true } when an ad with audio starts playing.
  2. When the ad ends, pauses, or is dismissed, the SDK calls it again with { muted: false }.
  3. Your callback is responsible for actually muting the host <video> element (or your third-party player).

Basic implementation

const videoPlayerController: VideoPlayerCallback = useCallback((data) => {
  if (!videoRef.current) return

  if (data.muted !== undefined) {
    videoRef.current.muted = data.muted
  }
}, [])

Implementation with volume reduction

If you prefer to reduce volume instead of fully muting, you can lower the volume and restore it when the ad finishes:

const savedVolume = useRef(1)

const videoPlayerController: VideoPlayerCallback = useCallback((data) => {
  if (!videoRef.current) return

  if (data.muted === true) {
    savedVolume.current = videoRef.current.volume
    videoRef.current.volume = 0.1 // reduce to 10%
  } else if (data.muted === false) {
    videoRef.current.volume = savedVolume.current
  }
}, [])

Important: Always pass the videoPlayerController callback alongside externalAd when using external VAST ads. Without it, the host video and the ad will play audio simultaneously, resulting in a poor user experience.


Related