diff --git a/srgmediaplayer/src/main/java/ch/srg/mediaplayer/AkamaiMediaAnalyticsConfiguration.java b/srgmediaplayer/src/main/java/ch/srg/mediaplayer/AkamaiMediaAnalyticsConfiguration.java new file mode 100644 index 0000000..1bd94a8 --- /dev/null +++ b/srgmediaplayer/src/main/java/ch/srg/mediaplayer/AkamaiMediaAnalyticsConfiguration.java @@ -0,0 +1,16 @@ +package ch.srg.mediaplayer; + +import android.util.Pair; + +/** + * Copyright (c) SRG SSR. All rights reserved. + *

+ * License information is available from the LICENSE file. + */ +public interface AkamaiMediaAnalyticsConfiguration { + String getAkamaiMediaAnalyticsConfigUrl(); + + String getAkamaiMediaAnalyticsViewerId(); + + Iterable> getAkamaiMediaAnalyticsDataSet(); +} diff --git a/srgmediaplayer/src/main/java/ch/srg/mediaplayer/EventLogger.java b/srgmediaplayer/src/main/java/ch/srg/mediaplayer/EventLogger.java index 17242cc..3a8638e 100644 --- a/srgmediaplayer/src/main/java/ch/srg/mediaplayer/EventLogger.java +++ b/srgmediaplayer/src/main/java/ch/srg/mediaplayer/EventLogger.java @@ -16,9 +16,11 @@ package ch.srg.mediaplayer; import android.os.SystemClock; +import android.support.annotation.Nullable; import android.util.Log; import android.view.Surface; +import com.akamai.android.exoplayer2loader.AkamaiExoPlayerLoader; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlayer; @@ -77,6 +79,8 @@ private final Timeline.Window window; private final Timeline.Period period; private final long startTimeMs; + @Nullable + private AdaptiveMediaSourceEventListener chainedListener; public EventLogger(MappingTrackSelector trackSelector) { this.trackSelector = trackSelector; @@ -330,6 +334,9 @@ public void onLoadStarted(DataSpec dataSpec, int dataType, int trackType, Format int trackSelectionReason, Object trackSelectionData, long mediaStartTimeMs, long mediaEndTimeMs, long elapsedRealtimeMs) { // Do nothing. + if (chainedListener != null) { + chainedListener.onLoadStarted(dataSpec, dataType, trackType, trackFormat, trackSelectionReason, trackSelectionData, mediaStartTimeMs, mediaEndTimeMs, elapsedRealtimeMs); + } } @Override @@ -338,6 +345,9 @@ public void onLoadError(DataSpec dataSpec, int dataType, int trackType, Format t long mediaEndTimeMs, long elapsedRealtimeMs, long loadDurationMs, long bytesLoaded, IOException error, boolean wasCanceled) { printInternalError("loadError", error); + if (chainedListener != null) { + chainedListener.onLoadError(dataSpec, dataType, trackType, trackFormat, trackSelectionReason, trackSelectionData, mediaStartTimeMs, mediaEndTimeMs, elapsedRealtimeMs, loadDurationMs, bytesLoaded, error, wasCanceled); + } } @Override @@ -345,6 +355,9 @@ public void onLoadCanceled(DataSpec dataSpec, int dataType, int trackType, Forma int trackSelectionReason, Object trackSelectionData, long mediaStartTimeMs, long mediaEndTimeMs, long elapsedRealtimeMs, long loadDurationMs, long bytesLoaded) { // Do nothing. + if (chainedListener != null) { + chainedListener.onLoadCanceled(dataSpec, dataType, trackType, trackFormat, trackSelectionReason, trackSelectionData, mediaStartTimeMs, mediaEndTimeMs, elapsedRealtimeMs, loadDurationMs, bytesLoaded); + } } @Override @@ -352,17 +365,26 @@ public void onLoadCompleted(DataSpec dataSpec, int dataType, int trackType, Form int trackSelectionReason, Object trackSelectionData, long mediaStartTimeMs, long mediaEndTimeMs, long elapsedRealtimeMs, long loadDurationMs, long bytesLoaded) { Log.d(TAG, "onLoadCompleted: mediaStartTimeMs:" + mediaStartTimeMs + " mediaEndTimeMs:" + mediaEndTimeMs + " elapsedRealtimeMs:" + elapsedRealtimeMs); + if (chainedListener != null) { + chainedListener.onLoadCompleted(dataSpec, dataType, trackType, trackFormat, trackSelectionReason, trackSelectionData, mediaStartTimeMs, mediaEndTimeMs, elapsedRealtimeMs, loadDurationMs, bytesLoaded); + } } @Override public void onUpstreamDiscarded(int trackType, long mediaStartTimeMs, long mediaEndTimeMs) { // Do nothing. + if (chainedListener != null) { + chainedListener.onUpstreamDiscarded(trackType, mediaStartTimeMs, mediaEndTimeMs); + } } @Override public void onDownstreamFormatChanged(int trackType, Format trackFormat, int trackSelectionReason, Object trackSelectionData, long mediaTimeMs) { // Do nothing. + if (chainedListener != null) { + chainedListener.onDownstreamFormatChanged(trackType, trackFormat, trackSelectionReason, trackSelectionData, mediaTimeMs); + } } // Internal methods @@ -471,4 +493,8 @@ private static String getTrackStatusString(boolean enabled) { return enabled ? "[X]" : "[ ]"; } + @Nullable + public void setChainedListener(AdaptiveMediaSourceEventListener chainedListener) { + this.chainedListener = chainedListener; + } } diff --git a/srgmediaplayer/src/main/java/ch/srg/mediaplayer/SRGMediaPlayerController.java b/srgmediaplayer/src/main/java/ch/srg/mediaplayer/SRGMediaPlayerController.java index b3de357..bb781d6 100644 --- a/srgmediaplayer/src/main/java/ch/srg/mediaplayer/SRGMediaPlayerController.java +++ b/srgmediaplayer/src/main/java/ch/srg/mediaplayer/SRGMediaPlayerController.java @@ -65,7 +65,6 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.UUID; import java.util.WeakHashMap; import ch.srg.mediaplayer.segment.model.Segment; @@ -445,8 +444,12 @@ public interface Listener { private static Set globalEventListeners = Collections.newSetFromMap(new WeakHashMap()); + @Nullable private AkamaiExoPlayerLoader akamaiExoPlayerLoader; + @Nullable + private AkamaiMediaAnalyticsConfiguration akamaiMediaAnalyticsConfiguration; + /** * Create a new SRGMediaPlayerController with the current context, a mediaPlayerDataProvider, and a TAG * if you need to retrieve a controller @@ -484,14 +487,6 @@ public SRGMediaPlayerController(Context context, String tag) { audioFocusChangeListener = new OnAudioFocusChangeListener(new WeakReference<>(this)); audioFocusGranted = false; - - String AKAMAI_QOS_VIEWER_ID = UUID.randomUUID().toString(); //FIXME Generate for each exoplayer session or shared between all exoplayer for a device? - String AKAMAI_QOS_CONFIG_URL = "https://ma252-r.analytics.edgekey.net/config/beacon-17838.xml"; - akamaiExoPlayerLoader = new AkamaiExoPlayerLoader(getContext(), "AKAMAI_QOS_CONFIG_URL", true); - akamaiExoPlayerLoader.setViewerId(AKAMAI_QOS_VIEWER_ID); - akamaiExoPlayerLoader.setViewerDiagnosticsId(AKAMAI_QOS_VIEWER_ID); - - exoPlayer.setVideoDebugListener(akamaiExoPlayerLoader); } private synchronized void startBackgroundThreadIfNecessary() { @@ -861,9 +856,9 @@ public void run() { } } - private void prepareInternal(Uri videoUri, int streamType) throws SRGMediaPlayerException { + private void prepareInternal(@NonNull Uri videoUri, int streamType) throws SRGMediaPlayerException { Log.v(TAG, "Preparing " + videoUri + " (" + streamType + ")"); - akamaiExoPlayerLoader.initializeLoader(exoPlayer, videoUri.toString()); + setupAkamaiQos(videoUri); try { if (this.currentMediaUri != null && this.currentMediaUri.equals(videoUri)) { return; @@ -884,13 +879,15 @@ private void prepareInternal(Uri videoUri, int streamType) throws SRGMediaPlayer MediaSource mediaSource; + eventLogger.setChainedListener(akamaiExoPlayerLoader); + switch (streamType) { case STREAM_DASH: mediaSource = new DashMediaSource(videoUri, dataSourceFactory, - new DefaultDashChunkSource.Factory(dataSourceFactory), mainHandler, akamaiExoPlayerLoader); + new DefaultDashChunkSource.Factory(dataSourceFactory), mainHandler, eventLogger); break; case STREAM_HLS: - mediaSource = new HlsMediaSource(videoUri, dataSourceFactory, mainHandler, akamaiExoPlayerLoader); + mediaSource = new HlsMediaSource(videoUri, dataSourceFactory, mainHandler, eventLogger); break; case STREAM_HTTP_PROGRESSIVE: mediaSource = new ExtractorMediaSource(videoUri, dataSourceFactory, new DefaultExtractorsFactory(), @@ -912,6 +909,18 @@ private void prepareInternal(Uri videoUri, int streamType) throws SRGMediaPlayer } } + private void setupAkamaiQos(@NonNull Uri videoUri) { + if (akamaiMediaAnalyticsConfiguration != null) { + akamaiExoPlayerLoader = new AkamaiExoPlayerLoader(getContext(), akamaiMediaAnalyticsConfiguration.getAkamaiMediaAnalyticsConfigUrl(), isDebugMode()); + akamaiExoPlayerLoader.setViewerId(akamaiMediaAnalyticsConfiguration.getAkamaiMediaAnalyticsViewerId()); + for (Pair keyValue : akamaiMediaAnalyticsConfiguration.getAkamaiMediaAnalyticsDataSet()) { + akamaiExoPlayerLoader.setData(keyValue.first, keyValue.second); + } + akamaiExoPlayerLoader.initializeLoader(exoPlayer, videoUri.toString()); + exoPlayer.setVideoDebugListener(akamaiExoPlayerLoader); + } + } + private void muteInternal(boolean muted) { exoPlayer.setVolume(muted ? 0f : 1f); } @@ -1111,7 +1120,9 @@ private void releaseDelegateInternal() { } postEventInternal(Event.Type.MEDIA_STOPPED); - akamaiExoPlayerLoader.releaseLoader(); + if (akamaiExoPlayerLoader != null) { + akamaiExoPlayerLoader.releaseLoader(); + } exoPlayer.stop(); exoPlayer.release(); currentMediaUri = null; @@ -2007,4 +2018,13 @@ public void onCues(List cues) { sendMessage(MSG_PLAYER_SUBTITLE_CUES, cues); } + /** + * Provide Akamai QOS Configuration. + * + * @param akamaiMediaAnalyticsConfiguration akamai qos configuration to enable QOS monitoring. null to disable + * akamai qos. + */ + public void setAkamaiMediaAnalyticsConfiguration(@Nullable AkamaiMediaAnalyticsConfiguration akamaiMediaAnalyticsConfiguration) { + this.akamaiMediaAnalyticsConfiguration = akamaiMediaAnalyticsConfiguration; + } } \ No newline at end of file