TVSDK fornisce API e codice di esempio per la gestione dei periodi di sospensione attività.
Per implementare la gestione delle sospensioni attività, inclusa la fornitura di contenuto alternativo durante la sospensione attività:
Configura l'app per rilevare i tag di sospensione attività in un manifesto live stream.
public void createMediaPlayer {
...
String[] blackoutTags = {BLACKOUTTAG};
PSDKConfig.setSubscribedTags(blackoutTags);
// For example: PTSDKConfig.setSubscribedTags({"#EXT-OATCLS-SCTE35"});
}
Creare listener di eventi per eventi di metadati temporizzati nei flussi in primo piano e in background.
private MediaPlayer createMediaPlayer() {
mediaPlayer.addEventListener(MediaPlayer.Event.PLAYBACK, _playbackEventListener);
mediaPlayer.addEventListener(MediaPlayer.Event.BLACKOUTS, _blackoutsEventListener);
}
Implementa gestori eventi metadati temporizzati per flussi in primo piano e in background.
Primo piano:
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() {
...
}
};
Maniglia TimedMetadata
oggetti quando MediaPlayer
il tempo scorre.
_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);
}
}
}
});
}
};
Creare metodi per cambiare il contenuto all'inizio e alla fine del periodo di sospensione attività.
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;
}
}
}
}
Aggiornare gli intervalli non ricercabili se l'intervallo di sospensione attività è in DVR nel flusso di riproduzione.
// 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];
}
}
}
}
Attualmente, per flussi live con velocità di trasmissione multipla, occasionalmente i profili ABR (bit-rate regolabile) possono non essere sincronizzati. Questo causa la duplicazione timedMetadata
per lo stesso tag sottoscritto. Per evitare calcoli non ricercabili errati, si consiglia vivamente di verificare la presenza di intervalli non ricercabili sovrapposti dopo i calcoli, ad esempio nell’esempio seguente:
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);
}