Implementación de gestión de interrupciones

TVSDK proporciona API y código de ejemplo para gestionar períodos de interrupción.

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

  1. Configure la aplicación para detectar etiquetas de interrupción 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 detectores de eventos para eventos de metadatos cronometrados 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 cronometrados para las secuencias 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. Handle TimedMetadata objetos cuando MediaPlayer el tiempo corre.

    _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 el contenido al principio y al final del período 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 los 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 causa un duplicado timedMetadata objetos para la misma etiqueta suscrita. Para evitar cálculos incorrectos que no se puedan buscar, se recomienda encarecidamente comprobar si hay intervalos no buscables 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