Implementar la gestión de bloqueos

TVSDK proporciona API y código de muestra para la gestión de periodos de interrupción.

Para implementar la gestión de bloqueos, incluido el suministro de contenido alternativo durante la interrupción:

  1. Configure la aplicación para detectar etiquetas de bloqueo en un manifiesto de flujo en directo.

    public void createMediaPlayer {
        ...
        String[] blackoutTags = {BLACKOUTTAG};
        PSDKConfig.setSubscribedTags(blackoutTags);
        // For example: PTSDKConfig.setSubscribedTags({"#EXT-OATCLS-SCTE35"});
    }
    
  2. Cree oyentes de eventos para eventos de metadatos temporizados en flujos en primer y segundo plano.

    private MediaPlayer createMediaPlayer() {
        mediaPlayer.addEventListener(MediaPlayer.Event.PLAYBACK, _playbackEventListener);
        mediaPlayer.addEventListener(MediaPlayer.Event.BLACKOUTS, _blackoutsEventListener);
    }
    
  3. Implemente controladores de eventos de metadatos temporizados para flujos en primer y segundo plano.

    Primer plano:

    private final MediaPlayer.PlaybackEventListener _playbackEventListener =
              new MediaPlayer.PlaybackEventListener() {
        ...
    
        @override
        public void onTimedMetadata(TimedMetadata timedMetadata) {
            if (timedMetadata.getName().equal(BLACKOUTTAG) &&
                !_timedMetadataList.contains(timedMetadata)) {
                  _timedMetadataList.add(timedMetadata);
            }
        }
        ...
    }
    
    private final MediaPlayer.BlackoutsEventListener _blackoutsEventListener =
      new MediaPlayer.BlackoutsEventListener() {
        @Override
        public void onTimedMetadataInBackgroundItem(TimedMetadata timedMetadata) {
            TimedMetadata.Type type = timedMetadata.getType();
            if (type.equals(TimedMetadata.Type.TAG) && _mediaPlayer.getPlaybackRange() != null
                && _mediaPlayer.getPlaybackRange().getDuration() > 0) {
                if (!_timedMetadataList.contains(timedMetadata) && isBlackoutMetadata(timedMetadata)) {
                    _timedMetadataList.add(timedMetadata);
                }
            }
        }
    
        @Override
        public void onBackgroundManifestFailed() {
            ...
        }
    };
    
  4. Gestione los objetos TimedMetadata cuando se ejecute el tiempo MediaPlayer .

    _playbackClockEventListener = new Clock.ClockEventListener() {
        @Override
        public void onTick(String name) {
            getActivity().runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    /* handle timedmetadata object list  */
                    if (_mediaPlayer != null && _timedMetadataList != null
                        && _timedMetadataList.size() > 0) {
                        if (_lastKnownStatus == MediaPlayer.PlayerState.PLAYING) {
                            long localTime = _mediaPlayer.getLocalTime();
                            handleTimedMetadataList(localTime);
                        }
                    }
                }
            });
        }
    };
    
  5. Cree métodos para cambiar contenido al principio y al final del periodo de interrupción.

    private void handleTimedMetadataList(long currentTime) {
        for (int i = 0; i < _timedMetadataList.size(); i++) {
            TimedMetadata timedMetadata = _timedMetadataList.get(i);
            long diff = localTime - timedMetadata.getTime();
            if (!_timedMetadataDispatchedList.contains(timedMetadata)
                && diff >= 0
                && diff <= PLAYBACK_CLOCK_INTERVAL
                && _mediaPlayer.shouldTriggerSubscribedTagEvent()) {
                    // switch to blackout content
                if (!_inBlackout && isBlackoutStartTimedMetadata(timedMetadata)) {
                    MediaResource blackoutMediaResource = createBlackoutMediaResource(timedMetadata);
    
                    //1. register current item as background item
                    _mediaPlayer.registerCurrentItemAsBackgroundItem();
    
                    //2. replace current item with blackout item
                    _mediaPlayer.replaceCurrentItem(blackoutMediaResource);
    
                    //3. update qos metrics
                    _mediaQosProvider.updateMetrics(_mediaPlayer);
    
                    //4. maintain state
                    _inBlackout = true;
                    resetTimedMetada();
    
                    break;
                }
                // switch back to main content
                else if (_inBlackout && isBlackoutEndTimedMetadata(timedMetadata)) {
                    //1. register current item as background item
                    _mediaPlayer.unregisterCurrentBackgroundItem();
    
                    //2. replace current item with blackout item
                    _mediaPlayer.replaceCurrentItem(_oldMediaResource);
    
                    //3. update qos metrics
                    _mediaQosProvider.updateMetrics(_mediaPlayer);
    
                    //4. maintain state
                    _inBlackout = false;
                    resetTimedMetada();
    
                    break;
                }
            }
        }
    }
    
  6. Actualice rangos no buscables si el rango de interrupción está en DVR en el flujo de reproducción.

    // prepare and update blackout nonSeekable ranges
    
    List<TimeRange> blackoutRanges = prepareBlackoutRanges(_timedMetadataList);
    if (blackoutRanges != null && blackoutRanges.size() > 0) {
        int size = blackoutRanges.size();
        TimeRange[] blackoutRangesArray = blackoutRanges.toArray(new TimeRange[size]);
        BlackoutMetadata blackoutMetadata = new BlackoutMetadata(blackoutRangesArray);
        updateBlackoutMetadata(blackoutMetadata);
    }
    
    // function to update blackout metadata
    private void updateBlackoutMetadata(BlackoutMetadata blackoutMetadata) {
        MediaPlayerItem currentItem = _mediaPlayer.getCurrentItem();
        if (currentItem != null) {
            Metadata metadata = currentItem.getResource().getMetadata();
            if (metadata != null) {
                MetadataNode metadataNode = ((MetadataNode) metadata);
                metadataNode.setNode(DefaultMetadataKeys.BLACKOUT_METADATA_KEY.getValue(),
                                     blackoutMetadata);
    
                for (int i = 0; i < blackoutMetadata.getNonSeekableRanges().length; i++) {
                    TimeRange timeRange = blackoutMetadata.getNonSeekableRanges()[i];
                }
            }
        }
    }
    
    NOTA

    Actualmente, para varios flujos en directo de velocidad de bits, ocasionalmente los perfiles de velocidad de bits ajustable (ABR) pueden no estar sincronizados. Esto provoca objetos timedMetadata duplicados para la misma etiqueta suscrita. Para evitar cálculos incorrectos que no se pueden buscar, es muy recomendable comprobar si hay intervalos que no se pueden buscar superpuestos después de los cálculos, como en el siguiente ejemplo:

    List<TimeRange> rangesToRemove = new ArrayList<TimeRange>();
    
    for (int i = 0; i < nonSeekableRanges.size() - 1; i++) {
        TimeRange range1 = nonSeekableRanges.get(i);
        TimeRange range2 = nonSeekableRanges.get(i + 1);
        if (range1.contains(range2.getBegin()) && !rangesToRemove.contains(range2)) {
           rangesToRemove.add(range2);
        } else if (range2.contains(range1.getBegin()) && !rangesToRemove.contains(range1)) {
           rangesToRemove.add(range1);
       }
    }
    
    if (nonSeekableRanges.size() > 0 && rangesToRemove.size() > 0) {
        nonSeekableRanges.removeAll(rangesToRemove);
        for (int i = 0; i < rangesToRemove.size(); i++) {
            TimeRange range = rangesToRemove.get(i);
        }
    }
    
    if (nonSeekableRanges.size() > 0 && rangesToRemove.size() > 0) {
        nonSeekableRanges.removeAll(rangesToRemove);
    }
    

En esta página