Implement blackout handling

TVSDK provides APIs and sample code for handling blackout periods.

To implement blackout handling including providing alternate content during the blackout:

  1. Set up your app to detect blackout tags in a live stream manifest.

    private function startPlayback(resource:MediaResource):void { 
        ... 
        var tags:Vector.<String> = PSDKConfig.retrieveMediaPlayerItemConfig().subscribeTags; 
            tags.push(BlackoutHandler.BLACK_OUT_START_TAG); 
            tags.push(BlackoutHandler.BLACK_OUT_END_TAG); 
      
        PSDKConfig.retrieveMediaPlayerItemConfig().subscribeTags = tags; 
        ... 
    }
    
  2. Create event listeners for timed metadata events in foreground and background streams.

    private function createMediaPlayer(context:MediaPlayerContext):void { 
        ... 
        _player.addEventListener(TimedMetadataEvent.TIMED_METADATA_AVAILABLE, onTimedMetadata); 
        _player.addEventListener(TimedMetadataEvent.TIMED_METADATA_IN_BACKGROUND_AVAILABLE,  
                                 onTimedMetadataInBackground); 
        ... 
    }
    
  3. Implement timed metadata event handlers for both foreground and background streams.

    Foreground:

    private function onTimedMetadata(event:TimedMetadataEvent):void { 
        var timedMetadata:TimedMetadata = event.timedMetadata; 
        var start:uint = 0; 
        var end:uint = 0; 
        if (timedMetadata.type == TimedMetadataType.TAG) { 
            if (timedMetadata.name == BLACK_OUT_START_TAG) { 
                start = timedMetadata.time; 
                end = start + _player.playbackRange.end; // Get the duration 
                _blackoutRanges.push(new BlackoutMetadata(new TimeRange(start, end))); 
            } 
        } 
    }
    

    Background:

    private function onTimedMetadataInBackground(event:TimedMetadataEvent):void { 
        var timedMetadata:TimedMetadata = event.timedMetadata; 
        if (timedMetadata.type == TimedMetadataType.TAG) { 
            if (timedMetadata.name == BLACK_OUT_START_TAG) 
                _blackoutStartTime = timedMetadata.time; 
              
            if (timedMetadata.name == BLACK_OUT_END_TAG) { 
                _logger.debug("#onTimedMetadataInBackground New blackout end tag identified."); 
                _blackoutENDTime = timedMetadata.time; 
                var duration:int = ((_blackoutENDTime - _blackoutStartTime) - _offset) - _interval; 
                if (duration > 0) { 
                    _logger.debug("#onTimedMetadataInBackground Blackout {0} msec of blackout remaining  
                                  based on new/updated tags.", duration); 
                    _countdownTimer.reset(); 
                    _countdownTimer.delay = duration; 
                    _countdownTimer.start() 
                } 
            } 
        } 
    }
    
  4. Prepare the MediaPlayer for blackouts.

    public function prepareBlackoutRanges(timedMetadata:Vector.<TimedMetadata>):void { 
        _logger.debug('#prepareBlackoutRanges Calculating blackout ranges in DVR window'); 
        var start:uint = 0; 
        var end:uint = 0; 
        for (var i:uint = 0; i < timedMetadata.length; i++) { 
            if (timedMetadata[i].type == TimedMetadataType.TAG) { 
                if (timedMetadata[i].name == BLACK_OUT_END_TAG) { 
                    end = timedMetadata[i].time; 
                    if (start == 0) { 
                        _blackoutRanges.push(new TimeRange(0, end)); // Do we have any orphan end tags? 
                        end = 0; 
                    } 
                    else { 
                        _blackoutRanges.push(new TimeRange(start, end)); // If not... 
                        start = 0; 
                        end = 0; 
                    } 
                } 
                if (timedMetadata[i].name == BLACK_OUT_START_TAG) { 
                    start = timedMetadata[i].time; // Get the start time 
                    if (timedMetadata.length - 1 == i) 
                        _blackoutRanges.push(new TimeRange(start, _player.playbackRange.end)); 
                } 
            } 
        } 
         
        var blackoutRangeMetadata:BlackoutMetadata = new BlackoutMetadata(_blackoutRanges); 
        // Set the list of blackout ranges on the MediaPlayer 
        var metadata:Metadata = _player.currentItem.resource.metadata; 
        if(metadata) 
            metadata.setObject(DefaultMetadataKeys.BLACKOUT_METADATA_KEY, blackoutRangeMetadata); 
        _logger.debug("#prepareBlackoutRanges Printing DefaultMediaPlayer's representation of timedBlackoutMetadata."); 
    }
    
  5. Set up a check of the list of TimedMetadataObjects for each occurrence of an update to the playhead position.

    private function onTimeChange(event:TimeChangeEvent):void { 
        if (!_inBlackout) { 
            for (var i:int = _blackoutRanges.length - 1; i >= 0; i--) { 
                if (containsTime(event.time) && !_seeking && !_inBlackout && !_adPlaying) { 
                    var index:int = getIndexForTime(event.time); 
                    if (_blackoutRanges[index].nonSeekableRange.end > _player.seekableRange.end) { 
                        _startTimer.reset(); 
                        _startTimer.start(); 
                        _offset = _player.currentTime - _blackoutRanges[index].nonSeekableRange.begin; 
                        var duration:int = _blackoutRanges[index].nonSeekableRange.duration -  
                                             (event.time - _blackoutRanges[index].nonSeekableRange.begin); 
                        _logger.debug("#onTimeChange Main content will resume in {0} msec.", duration); 
                        _blackoutStartTime = _blackoutRanges[index].nonSeekableRange.begin; 
                        initiate(); 
                        return; 
                    } else { 
                        _logger.debug("#onTimeChange Live blackout range start dectected."); 
                        _logger.debug("#onTimeChange Seekable range end {0}, Playable range {1},  
                                      Playhead {2}", _player.seekableRange.end,  
                                      _player.playbackRange.toString(), event.time); 
                        _player.seek(_blackoutRanges[index].nonSeekableRange.end); 
                        _seeking = true; 
                        return; 
                    } 
                } 
            } 
        } 
    }
    
  6. Create methods for switching content at the start and end of the blackout period.

    public function initiate(event:TimerEvent=null):void { 
        _inBlackout = true; 
        _logger.debug("#initiate Entering blackout"); 
        _player.removeEventListener(TimedMetadataEvent.TIMED_METADATA_AVAILABLE, onTimedMetadata); 
        _player.removeEventListener(TimeChangeEvent.TIME_CHANGED, onTimeChange); 
          
        // Register the current (original playback item) in background. 
        _player.registerCurrentItemAsBackgroundItem(); 
      
        // Replace the current playback item with the alternate stream. 
        _player.replaceCurrentResource(_alternate); 
    } 
      
    public function terminate(event:TimerEvent=null):void { 
        _inBlackout = false; 
        _offset = 0; 
        _logger.debug("#terminate Exiting blackout."); 
        _blackoutRanges.length = 0; 
        _blackoutRanges = new Vector.<BlackoutMetadata>(); 
        // Unregister background resource 
        _player.unregisterCurrentBackgroundItem(); 
        // Switch back to the original resource 
        _player.replaceCurrentResource(mainResource); 
        _player.addEventListener(TimedMetadataEvent.TIMED_METADATA_AVAILABLE, onTimedMetadata); 
        _player.addEventListener(TimeChangeEvent.TIME_CHANGED, onTimeChange); 
        _seekToBlackoutEnd = true; 
        // Reset countdown timer 
        if (_countdownTimer) { 
            _countdownTimer.reset(); 
            _startTimer.stop(); 
            _startTimer.reset(); 
            _interval = 0; 
        } 
    }
    

On this page

Adobe Summit Banner

A virtual event April 27-28.

Expand your skills and get inspired.

Register for free
Adobe Summit Banner

A virtual event April 27-28.

Expand your skills and get inspired.

Register for free
Adobe Maker Awards Banner

Time to shine!

Apply now for the 2021 Adobe Experience Maker Awards.

Apply now
Adobe Maker Awards Banner

Time to shine!

Apply now for the 2021 Adobe Experience Maker Awards.

Apply now