From 2e557189bffe9846b98e9accd240a5200d47824b Mon Sep 17 00:00:00 2001 From: Noam Tamim Date: Wed, 15 Jan 2020 10:50:50 +0200 Subject: [PATCH 01/14] Report the current bandwidth for later analysis --- .../playkit/player/ExoPlayerWrapper.java | 87 ++++++++++++++++++- .../com/kaltura/playkit/player/Profiler.java | 2 + .../playkit/profiler/PlayKitProfiler.java | 31 +++++-- 3 files changed, 111 insertions(+), 9 deletions(-) diff --git a/playkit/src/main/java/com/kaltura/playkit/player/ExoPlayerWrapper.java b/playkit/src/main/java/com/kaltura/playkit/player/ExoPlayerWrapper.java index ddf1d3018..f78468b1f 100644 --- a/playkit/src/main/java/com/kaltura/playkit/player/ExoPlayerWrapper.java +++ b/playkit/src/main/java/com/kaltura/playkit/player/ExoPlayerWrapper.java @@ -16,7 +16,10 @@ import android.net.Uri; import android.os.Handler; import android.os.Looper; +import android.os.SystemClock; + import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.kaltura.android.exoplayer2.C; import com.kaltura.android.exoplayer2.DefaultLoadControl; @@ -48,12 +51,14 @@ import com.kaltura.android.exoplayer2.upstream.BandwidthMeter; import com.kaltura.android.exoplayer2.upstream.DataSource; import com.kaltura.android.exoplayer2.upstream.DefaultAllocator; +import com.kaltura.android.exoplayer2.upstream.DataSpec; import com.kaltura.android.exoplayer2.upstream.DefaultBandwidthMeter; import com.kaltura.android.exoplayer2.upstream.DefaultDataSourceFactory; import com.kaltura.android.exoplayer2.upstream.DefaultHttpDataSource; import com.kaltura.android.exoplayer2.upstream.DefaultHttpDataSourceFactory; import com.kaltura.android.exoplayer2.upstream.HttpDataSource; import com.kaltura.android.exoplayer2.video.CustomLoadControl; +import com.kaltura.android.exoplayer2.upstream.TransferListener; import com.kaltura.playkit.*; import com.kaltura.playkit.drm.DeferredDrmSessionManager; import com.kaltura.playkit.drm.DrmCallback; @@ -66,6 +71,7 @@ import java.net.CookieManager; import java.net.CookiePolicy; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -159,7 +165,8 @@ public interface LoadControlStrategy { bandwidthMeterBuilder.setInitialBitrateEstimate(initialBitrateEstimate); } - bandwidthMeter = bandwidthMeterBuilder.build(); + bandwidthMeter = new MyBandwidthMeter(bandwidthMeterBuilder.build()); + } if (bandwidthMeter != null) { bandwidthMeter.addEventListener(mainHandler, this); @@ -1220,4 +1227,82 @@ private boolean assertPlayerIsNotNull(String methodName) { log.w(String.format(nullPlayerMsgFormat, methodName)); return false; } + + private class MyBandwidthMeter implements BandwidthMeter, TransferListener { + + PKLog log = PKLog.get("ExoWrapperMeter"); + + DefaultBandwidthMeter exoMeter; + Map sizes = new HashMap<>(); + Map starts = new HashMap<>(); + + MyBandwidthMeter(DefaultBandwidthMeter defaultBandwidthMeter) { + exoMeter = defaultBandwidthMeter; + } + + @Override + public long getBitrateEstimate() { + return exoMeter.getBitrateEstimate(); + } + + @Override + @Nullable + public TransferListener getTransferListener() { + return this; + } + + @Override + public void addEventListener(Handler eventHandler, EventListener eventListener) { + exoMeter.addEventListener(eventHandler, eventListener); + } + + @Override + public void removeEventListener(EventListener eventListener) { + exoMeter.removeEventListener(eventListener); + } + + @Override + public void onTransferInitializing(DataSource source, DataSpec dataSpec, boolean isNetwork) { + exoMeter.onTransferInitializing(source, dataSpec, isNetwork); + } + + @Override + public void onTransferStart(DataSource source, DataSpec dataSpec, boolean isNetwork) { + exoMeter.onTransferStart(source, dataSpec, isNetwork); + sizes.put(source, 0L); + starts.put(source, SystemClock.elapsedRealtime()); + } + + @Override + public void onBytesTransferred(DataSource source, DataSpec dataSpec, boolean isNetwork, int bytesTransferred) { + exoMeter.onBytesTransferred(source, dataSpec, isNetwork, bytesTransferred); + final Long aLong = sizes.get(source); + if (aLong != null) { + sizes.put(source, aLong + bytesTransferred); + } + } + + @Override + public void onTransferEnd(DataSource source, DataSpec dataSpec, boolean isNetwork) { + exoMeter.onTransferEnd(source, dataSpec, isNetwork); + + final Long totalSize = sizes.get(source); + if (totalSize == null) { + return; + } + + final Long startTime = starts.get(source); + if (startTime == null) { + return; + } + + final long totalTime = SystemClock.elapsedRealtime() - startTime; + float bytesPerSecond = totalSize / (totalTime / 1000f); + + final long exoBytesPerSecond = exoMeter.getBitrateEstimate() / 8; + PKLog.d("CSVMeter", bytesPerSecond + "," + exoBytesPerSecond); + + profiler.logCustom("MeterReading", "csv={" + bytesPerSecond + "," + exoBytesPerSecond + "}"); + } + } } diff --git a/playkit/src/main/java/com/kaltura/playkit/player/Profiler.java b/playkit/src/main/java/com/kaltura/playkit/player/Profiler.java index ab8510d74..3af4781b2 100644 --- a/playkit/src/main/java/com/kaltura/playkit/player/Profiler.java +++ b/playkit/src/main/java/com/kaltura/playkit/player/Profiler.java @@ -28,4 +28,6 @@ public void onDurationChanged(long duration) {/*NOOP*/} public void onApplicationPaused() {/*NOOP*/} public void onApplicationResumed() {/*NOOP*/} + + public void logCustom(String event, String... strings) {/*NOOP*/} } diff --git a/playkit/src/main/java/com/kaltura/playkit/profiler/PlayKitProfiler.java b/playkit/src/main/java/com/kaltura/playkit/profiler/PlayKitProfiler.java index e6bda269c..271c8f0c3 100644 --- a/playkit/src/main/java/com/kaltura/playkit/profiler/PlayKitProfiler.java +++ b/playkit/src/main/java/com/kaltura/playkit/profiler/PlayKitProfiler.java @@ -66,8 +66,8 @@ public class PlayKitProfiler { private static final PKLog pkLog = PKLog.get("PlayKitProfiler"); // Dev mode: shorter logs, write to local file, always enable - private static final boolean devMode = false; - private static final int SEND_INTERVAL_DEV = 10; // in seconds + private static final boolean devMode = true; + private static final int SEND_INTERVAL_DEV = 60; // in seconds private static final int SEND_PERCENTAGE_DEV = 100; // always private static final int SEND_INTERVAL_PROD = 120; // 2 minutes @@ -102,6 +102,8 @@ public class PlayKitProfiler { long sessionStartTime; private String sessionId; + private int chunkCount = 0; + // We need a reference to the player, but make sure not to keep it alive. @Nullable private WeakReference playerEngine; @@ -319,6 +321,7 @@ private static void downloadConfig(Context context) { // Download try { + if (true) throw new IOException(); bytes = Utils.executeGet(CONFIG_URL, null); if (bytes == null || bytes.length == 0) { @@ -375,7 +378,7 @@ private static void parseConfig(byte[] bytes) { try { final ConfigFile configFile = new Gson().fromJson(new String(bytes), ConfigFile.class); postURL = configFile.putLogURL; - sendPercentage = configFile.sendPercentage; +// sendPercentage = configFile.sendPercentage; } catch (JsonParseException e) { pkLog.e("Failed to parse config", e); } @@ -415,10 +418,13 @@ private void sendLogChunk() { final String string = sb.toString(); + int chunkIndex = chunkCount; + chunkCount++; + if (Looper.myLooper() == ioHandler.getLooper()) { - postChunk(string); + postChunk(string, chunkIndex); } else { - ioHandler.post(() -> postChunk(string)); + ioHandler.post(() -> postChunk(string, chunkIndex)); } // TODO: 17/02/2019 what if there's no network when sending the log? @@ -443,14 +449,14 @@ private void sendLogChunk() { } } - private void postChunk(String string) { + private void postChunk(String string, int chunkIndex) { if (postURL == null) { pkLog.w("No POST URL"); return; } try { - Utils.executePost(postURL + "?mode=addChunk&sessionId=" + sessionId, string.getBytes(), null); + Utils.executePost(postURL + "?mode=addChunk&sessionId=" + sessionId + "&index=" + chunkIndex, string.getBytes(), null); } catch (IOException e) { // FIXME: 03/09/2018 Is it bad that we lost this log chunk? pkLog.e("Failed sending log", e); @@ -458,7 +464,7 @@ private void postChunk(String string) { } } - void log(String event, String... strings) { + public void log(String event, String... strings) { StringBuilder sb = startLog(event); logPayload(sb, strings); endLog(sb); @@ -577,12 +583,16 @@ public void onPrepareStarted(final PKMediaSourceConfig sourceConfig) { @Override public void newSession(final String sessionId, PlayerSettings playerSettings) { + pkLog.e("New session " + sessionId); + + if (sessionId != null) { // close current session closeSession(); } PlayKitProfiler.this.sessionId = sessionId; + PlayKitProfiler.this.chunkCount = 0; if (sessionId == null) { return; // the null profiler } @@ -653,6 +663,11 @@ public void onApplicationResumed() { log("onApplicationResumed"); } + @Override + public void logCustom(String event, String... strings) { + log(event, strings); + } + @Override public void onSetMedia(PKMediaConfig mediaConfig) { JsonObject json = new JsonObject(); From dac4f2cb40f6cccb74acc20ee323a7e96240f31f Mon Sep 17 00:00:00 2001 From: Noam Tamim Date: Wed, 15 Jan 2020 12:05:08 +0200 Subject: [PATCH 02/14] There's no need to log the readings They are already in BandwidthSample logs. --- .../playkit/player/ExoPlayerWrapper.java | 96 +++---------------- .../com/kaltura/playkit/player/Profiler.java | 2 - .../playkit/profiler/PlayKitProfiler.java | 5 - 3 files changed, 11 insertions(+), 92 deletions(-) diff --git a/playkit/src/main/java/com/kaltura/playkit/player/ExoPlayerWrapper.java b/playkit/src/main/java/com/kaltura/playkit/player/ExoPlayerWrapper.java index f78468b1f..29343dae9 100644 --- a/playkit/src/main/java/com/kaltura/playkit/player/ExoPlayerWrapper.java +++ b/playkit/src/main/java/com/kaltura/playkit/player/ExoPlayerWrapper.java @@ -16,10 +16,8 @@ import android.net.Uri; import android.os.Handler; import android.os.Looper; -import android.os.SystemClock; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import com.kaltura.android.exoplayer2.C; import com.kaltura.android.exoplayer2.DefaultLoadControl; @@ -51,15 +49,22 @@ import com.kaltura.android.exoplayer2.upstream.BandwidthMeter; import com.kaltura.android.exoplayer2.upstream.DataSource; import com.kaltura.android.exoplayer2.upstream.DefaultAllocator; -import com.kaltura.android.exoplayer2.upstream.DataSpec; import com.kaltura.android.exoplayer2.upstream.DefaultBandwidthMeter; import com.kaltura.android.exoplayer2.upstream.DefaultDataSourceFactory; import com.kaltura.android.exoplayer2.upstream.DefaultHttpDataSource; import com.kaltura.android.exoplayer2.upstream.DefaultHttpDataSourceFactory; import com.kaltura.android.exoplayer2.upstream.HttpDataSource; import com.kaltura.android.exoplayer2.video.CustomLoadControl; -import com.kaltura.android.exoplayer2.upstream.TransferListener; -import com.kaltura.playkit.*; +import com.kaltura.playkit.LocalAssetsManagerExo; +import com.kaltura.playkit.PKError; +import com.kaltura.playkit.PKLog; +import com.kaltura.playkit.PKMediaEntry; +import com.kaltura.playkit.PKMediaFormat; +import com.kaltura.playkit.PKRequestParams; +import com.kaltura.playkit.PlaybackInfo; +import com.kaltura.playkit.PlayerEvent; +import com.kaltura.playkit.PlayerState; +import com.kaltura.playkit.Utils; import com.kaltura.playkit.drm.DeferredDrmSessionManager; import com.kaltura.playkit.drm.DrmCallback; import com.kaltura.playkit.player.metadata.MetadataConverter; @@ -71,7 +76,6 @@ import java.net.CookieManager; import java.net.CookiePolicy; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -165,7 +169,7 @@ public interface LoadControlStrategy { bandwidthMeterBuilder.setInitialBitrateEstimate(initialBitrateEstimate); } - bandwidthMeter = new MyBandwidthMeter(bandwidthMeterBuilder.build()); + bandwidthMeter = bandwidthMeterBuilder.build(); } if (bandwidthMeter != null) { @@ -1227,82 +1231,4 @@ private boolean assertPlayerIsNotNull(String methodName) { log.w(String.format(nullPlayerMsgFormat, methodName)); return false; } - - private class MyBandwidthMeter implements BandwidthMeter, TransferListener { - - PKLog log = PKLog.get("ExoWrapperMeter"); - - DefaultBandwidthMeter exoMeter; - Map sizes = new HashMap<>(); - Map starts = new HashMap<>(); - - MyBandwidthMeter(DefaultBandwidthMeter defaultBandwidthMeter) { - exoMeter = defaultBandwidthMeter; - } - - @Override - public long getBitrateEstimate() { - return exoMeter.getBitrateEstimate(); - } - - @Override - @Nullable - public TransferListener getTransferListener() { - return this; - } - - @Override - public void addEventListener(Handler eventHandler, EventListener eventListener) { - exoMeter.addEventListener(eventHandler, eventListener); - } - - @Override - public void removeEventListener(EventListener eventListener) { - exoMeter.removeEventListener(eventListener); - } - - @Override - public void onTransferInitializing(DataSource source, DataSpec dataSpec, boolean isNetwork) { - exoMeter.onTransferInitializing(source, dataSpec, isNetwork); - } - - @Override - public void onTransferStart(DataSource source, DataSpec dataSpec, boolean isNetwork) { - exoMeter.onTransferStart(source, dataSpec, isNetwork); - sizes.put(source, 0L); - starts.put(source, SystemClock.elapsedRealtime()); - } - - @Override - public void onBytesTransferred(DataSource source, DataSpec dataSpec, boolean isNetwork, int bytesTransferred) { - exoMeter.onBytesTransferred(source, dataSpec, isNetwork, bytesTransferred); - final Long aLong = sizes.get(source); - if (aLong != null) { - sizes.put(source, aLong + bytesTransferred); - } - } - - @Override - public void onTransferEnd(DataSource source, DataSpec dataSpec, boolean isNetwork) { - exoMeter.onTransferEnd(source, dataSpec, isNetwork); - - final Long totalSize = sizes.get(source); - if (totalSize == null) { - return; - } - - final Long startTime = starts.get(source); - if (startTime == null) { - return; - } - - final long totalTime = SystemClock.elapsedRealtime() - startTime; - float bytesPerSecond = totalSize / (totalTime / 1000f); - - final long exoBytesPerSecond = exoMeter.getBitrateEstimate() / 8; - PKLog.d("CSVMeter", bytesPerSecond + "," + exoBytesPerSecond); - - profiler.logCustom("MeterReading", "csv={" + bytesPerSecond + "," + exoBytesPerSecond + "}"); - } - } } diff --git a/playkit/src/main/java/com/kaltura/playkit/player/Profiler.java b/playkit/src/main/java/com/kaltura/playkit/player/Profiler.java index 3af4781b2..ab8510d74 100644 --- a/playkit/src/main/java/com/kaltura/playkit/player/Profiler.java +++ b/playkit/src/main/java/com/kaltura/playkit/player/Profiler.java @@ -28,6 +28,4 @@ public void onDurationChanged(long duration) {/*NOOP*/} public void onApplicationPaused() {/*NOOP*/} public void onApplicationResumed() {/*NOOP*/} - - public void logCustom(String event, String... strings) {/*NOOP*/} } diff --git a/playkit/src/main/java/com/kaltura/playkit/profiler/PlayKitProfiler.java b/playkit/src/main/java/com/kaltura/playkit/profiler/PlayKitProfiler.java index 271c8f0c3..8d9ad5369 100644 --- a/playkit/src/main/java/com/kaltura/playkit/profiler/PlayKitProfiler.java +++ b/playkit/src/main/java/com/kaltura/playkit/profiler/PlayKitProfiler.java @@ -663,11 +663,6 @@ public void onApplicationResumed() { log("onApplicationResumed"); } - @Override - public void logCustom(String event, String... strings) { - log(event, strings); - } - @Override public void onSetMedia(PKMediaConfig mediaConfig) { JsonObject json = new JsonObject(); From 7ed8cde93d261d89a7bda567b5868209c26fc41a Mon Sep 17 00:00:00 2001 From: Noam Tamim Date: Tue, 12 May 2020 10:04:00 +0300 Subject: [PATCH 03/14] Profiler: refactor, allow runtime config --- .../main/java/com/kaltura/playkit/PKLog.java | 5 +- .../playkit/profiler/PlayKitProfiler.java | 393 +++++++++--------- 2 files changed, 192 insertions(+), 206 deletions(-) diff --git a/playkit/src/main/java/com/kaltura/playkit/PKLog.java b/playkit/src/main/java/com/kaltura/playkit/PKLog.java index 2442dc55d..8d1a6ccd1 100644 --- a/playkit/src/main/java/com/kaltura/playkit/PKLog.java +++ b/playkit/src/main/java/com/kaltura/playkit/PKLog.java @@ -63,7 +63,7 @@ public class PKLog { @NonNull public final String tag; - private int level = VERBOSE; + private int level; public enum Level { verbose(VERBOSE), debug(DEBUG), info(INFO), warn(WARN), error(ERROR), off(Integer.MAX_VALUE); @@ -103,6 +103,9 @@ private PKLog(@NonNull String tag) { return new PKLog(tag); } + public boolean isLoggable(Level level) { + return Log.isLoggable(tag, level.value); + } // VERBOSE diff --git a/playkit/src/main/java/com/kaltura/playkit/profiler/PlayKitProfiler.java b/playkit/src/main/java/com/kaltura/playkit/profiler/PlayKitProfiler.java index 8d9ad5369..33e814563 100644 --- a/playkit/src/main/java/com/kaltura/playkit/profiler/PlayKitProfiler.java +++ b/playkit/src/main/java/com/kaltura/playkit/profiler/PlayKitProfiler.java @@ -1,7 +1,6 @@ package com.kaltura.playkit.profiler; import android.content.Context; -import android.net.ConnectivityManager; import android.net.Uri; import android.os.Build; import android.os.Handler; @@ -12,7 +11,6 @@ import android.text.TextUtils; import android.util.DisplayMetrics; -import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.google.gson.Gson; @@ -40,8 +38,6 @@ import java.io.BufferedWriter; import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.lang.ref.WeakReference; @@ -66,19 +62,16 @@ public class PlayKitProfiler { private static final PKLog pkLog = PKLog.get("PlayKitProfiler"); // Dev mode: shorter logs, write to local file, always enable - private static final boolean devMode = true; - private static final int SEND_INTERVAL_DEV = 60; // in seconds + private static final boolean devMode = false; + private static final int SEND_INTERVAL_DEV = 60; // sec private static final int SEND_PERCENTAGE_DEV = 100; // always - private static final int SEND_INTERVAL_PROD = 120; // 2 minutes + private static final int SEND_INTERVAL_PROD = 60; // sec private static final int SEND_INTERVAL_SEC = devMode ? SEND_INTERVAL_DEV : SEND_INTERVAL_PROD; private static final float DEFAULT_SEND_PERCENTAGE = devMode ? SEND_PERCENTAGE_DEV : 0; // Start disabled - private static final String CONFIG_CACHE_FILENAME = "profilerConfig.json"; - private static final String CONFIG_URL = "https://s3.amazonaws.com/player-profiler/config.json"; - private static final String DEFAULT_POST_URL = "https://3vbje2fyag.execute-api.us-east-1.amazonaws.com/default/profilog"; - private static final int MAX_CONFIG_SIZE = 10240; + private static final String CONFIG_BASE_URL = "https://s3.amazonaws.com/player-profiler/configs/"; static final float MSEC_MULTIPLIER_FLOAT = 1000f; @@ -87,7 +80,7 @@ public class PlayKitProfiler { private static final Map experiments = new LinkedHashMap<>(); private static final int PERCENTAGE_MULTIPLIER = 100; // Configuration - private static String postURL = DEFAULT_POST_URL; + private static String postURL; private static float sendPercentage = DEFAULT_SEND_PERCENTAGE; // Static setup private static Handler ioHandler; @@ -95,6 +88,9 @@ public class PlayKitProfiler { private static DisplayMetrics metrics; private static File externalFilesDir; // for debug logs private static String packageName; + private static String networkType; + private static String deviceType; + private static boolean configLoaded; private final ConcurrentLinkedQueue logQueue = new ConcurrentLinkedQueue<>(); private final ExoPlayerProfilingListener analyticsListener = new ExoPlayerProfilingListener(this); private final EventListener.Factory okListenerFactory = call -> new OkHttpListener(PlayKitProfiler.this, call); @@ -108,6 +104,8 @@ public class PlayKitProfiler { @Nullable private WeakReference playerEngine; + private Profiler profilerImp; + private PlayKitProfiler() { ioHandler.post(new Runnable() { @@ -127,7 +125,11 @@ public void run() { * Initialize the static part of the profiler -- load the config and store it, * create IO thread and handler. Must be called by the app to enable the profiler. */ - public static void init(Context context) { + public static void init(Context context, String configToken) { + init(context, configToken, false); + } + + public static void init(Context context, String configToken, boolean waitForConfig) { // This only has to happen once. if (initialized) { @@ -143,14 +145,11 @@ public static void init(Context context) { final Context appContext = context.getApplicationContext(); - // Load cached config. Will load from network later, in a handler thread. - loadCachedConfig(appContext); - HandlerThread handlerThread = new HandlerThread("ProfilerIO", Process.THREAD_PRIORITY_BACKGROUND); handlerThread.start(); ioHandler = new Handler(handlerThread.getLooper()); - ioHandler.post(() -> downloadConfig(appContext)); + ioHandler.post(() -> downloadConfig(configToken)); initMembers(appContext); @@ -166,27 +165,26 @@ public static void init(Context context) { // random number is smaller than 0.05 a profiler is created and returned. // As a result, 5 in every 100 calls to the factory will create a real profiler. - ProfilerFactory.setFactory(() -> - Math.random() < sendPercentage / PERCENTAGE_MULTIPLIER ? new PlayKitProfiler().profilerImp : null); - } - } - - private static String getNetworkType(Context context) { - - final ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); - if (manager == null) { - return "Unknown"; - } - - switch (manager.getActiveNetworkInfo().getType()) { - case ConnectivityManager.TYPE_MOBILE: - return "Mobile"; - case ConnectivityManager.TYPE_WIFI: - return "Wifi"; - case ConnectivityManager.TYPE_ETHERNET: - return "Ethernet"; + ProfilerFactory.setFactory(() -> { + final double random = Math.random(); + final boolean enable = random < sendPercentage / PERCENTAGE_MULTIPLIER; + pkLog.d("Profiler enabled for session? " + enable); + if (enable) { + return new PlayKitProfiler().getProfilerImp(); + } + return null; + }); + + if (waitForConfig) { + while (!configLoaded) { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + pkLog.d("waitForConfig interrupted"); + } + } + } } - return null; } /** @@ -236,6 +234,9 @@ private static void initMembers(final Context context) { packageName = context.getPackageName(); metrics = context.getResources().getDisplayMetrics(); + networkType = Utils.getNetworkClass(context); + deviceType = Utils.getDeviceType(context); + if (devMode) { externalFilesDir = context.getExternalFilesDir(null); @@ -316,13 +317,12 @@ static String timeField(String name, long value) { return value == C.TIME_UNSET ? field(name, null) : field(name, value / MSEC_MULTIPLIER_FLOAT); } - private static void downloadConfig(Context context) { + private static void downloadConfig(String configToken) { final byte[] bytes; // Download try { - if (true) throw new IOException(); - bytes = Utils.executeGet(CONFIG_URL, null); + bytes = Utils.executeGet(CONFIG_BASE_URL + configToken + ".json", null); if (bytes == null || bytes.length == 0) { pkLog.w("Nothing returned from executeGet"); @@ -333,52 +333,15 @@ private static void downloadConfig(Context context) { } catch (IOException e) { pkLog.w("Failed to download config", e); - return; - } - - // Save to cache - final File cachedConfigFile = getCachedConfigFile(context); - if (cachedConfigFile.getParentFile().canWrite()) { - FileOutputStream outputStream = null; - try { - outputStream = new FileOutputStream(cachedConfigFile); - outputStream.write(bytes); - } catch (IOException e) { - pkLog.e("Failed to save config to cache", e); - } finally { - Utils.safeClose(outputStream); - } - } - } - - private static void loadCachedConfig(Context context) { - final File configFile = getCachedConfigFile(context); - - if (configFile.canRead()) { - FileInputStream inputStream = null; - try { - inputStream = new FileInputStream(configFile); - parseConfig(Utils.fullyReadInputStream(inputStream, MAX_CONFIG_SIZE).toByteArray()); - - } catch (IOException e) { - pkLog.e("Failed to read cached config file", e); - - } finally { - Utils.safeClose(inputStream); - } } } - @NonNull - private static File getCachedConfigFile(Context context) { - return new File(context.getFilesDir(), CONFIG_CACHE_FILENAME); - } - private static void parseConfig(byte[] bytes) { try { final ConfigFile configFile = new Gson().fromJson(new String(bytes), ConfigFile.class); - postURL = configFile.putLogURL; -// sendPercentage = configFile.sendPercentage; + postURL = configFile.postURL; + sendPercentage = configFile.sendPercentage; + configLoaded = true; } catch (JsonParseException e) { pkLog.e("Failed to parse config", e); } @@ -456,7 +419,12 @@ private void postChunk(String string, int chunkIndex) { } try { - Utils.executePost(postURL + "?mode=addChunk&sessionId=" + sessionId + "&index=" + chunkIndex, string.getBytes(), null); + final String url = postURL + "?mode=addChunk&sessionId=" + sessionId + "&index=" + chunkIndex; + if (pkLog.isLoggable(PKLog.Level.verbose)) { + pkLog.v("POST to " + url); + pkLog.v(string); + } + Utils.executePost(url, string.getBytes(), null); } catch (IOException e) { // FIXME: 03/09/2018 Is it bad that we lost this log chunk? pkLog.e("Failed sending log", e); @@ -557,159 +525,174 @@ void logWithPlaybackInfo(String event, String... strings) { } } - private Profiler profilerImp = new Profiler() { - @Override - public void setPlayerEngine(PlayerEngine engine) { - - if (engine instanceof ExoPlayerWrapper) { - playerEngine = new WeakReference<>(((ExoPlayerWrapper) engine)); - } else { - playerEngine = null; // other engines are not supported - } + private Profiler getProfilerImp() { + if (profilerImp == null) { + profilerImp = createProfilerImp(); } - @Override - public void onPrepareStarted(final PKMediaSourceConfig sourceConfig) { + return profilerImp; + } - final Uri sourceUrl = sourceConfig.getRequestParams().url; + private Profiler createProfilerImp() { - log("PrepareStarted", - field("engine", playerEngine.get().getClass().getSimpleName()), - field("source", sourceUrl.toString())); + pkLog.d("Creating a real profiler"); - maybeLogServerInfo(sourceUrl); - } + return new Profiler() { + @Override + public void setPlayerEngine(PlayerEngine engine) { - @Override - public void newSession(final String sessionId, PlayerSettings playerSettings) { + if (engine instanceof ExoPlayerWrapper) { + playerEngine = new WeakReference<>(((ExoPlayerWrapper) engine)); + } else { + playerEngine = null; // other engines are not supported + } + } - pkLog.e("New session " + sessionId); + @Override + public void onPrepareStarted(final PKMediaSourceConfig sourceConfig) { + final Uri sourceUrl = sourceConfig.getRequestParams().url; - if (sessionId != null) { - // close current session - closeSession(); - } + log("PrepareStarted", + field("engine", playerEngine.get().getClass().getSimpleName()), + field("source", sourceUrl.toString())); - PlayKitProfiler.this.sessionId = sessionId; - PlayKitProfiler.this.chunkCount = 0; - if (sessionId == null) { - return; // the null profiler + maybeLogServerInfo(sourceUrl); } - PlayKitProfiler.this.sessionStartTime = SystemClock.elapsedRealtime(); - PlayKitProfiler.this.logQueue.clear(); - - PlayKitProfiler.this.serversLookedUp.clear(); - - pkLog.d("New profiler with sessionId: " + sessionId); - - log("StartSession", - field("now", System.currentTimeMillis()), - field("strNow", new Date().toString()), - field("sessionId", sessionId), - field("packageName", packageName) - ); - - log("PlayKit", - field("version", PlayKitManager.VERSION_STRING), - field("clientTag", PlayKitManager.CLIENT_TAG) - ); - - log("Platform", - field("name", "Android"), - field("apiLevel", Build.VERSION.SDK_INT), - field("chipset", MediaSupport.DEVICE_CHIPSET), - field("brand", Build.BRAND), - field("model", Build.MODEL), - field("manufacturer", Build.MANUFACTURER), - field("device", Build.DEVICE), - field("tags", Build.TAGS), - field("fingerprint", Build.FINGERPRINT), - field("screenSize", metrics.widthPixels + "x" + metrics.heightPixels), - field("screenDpi", metrics.xdpi + "x" + metrics.ydpi) - ); - - log("PlayerSettings", - field("allowClearLead", playerSettings.allowClearLead()), - field("useTextureView", playerSettings.useTextureView())); - - final LoadControlBuffers loadControl = playerSettings.getLoadControlBuffers(); - if (loadControl != null) { - log("PlayerLoadControl", - field("minBufferLenMs", loadControl.getMinPlayerBufferMs()), - field("maxBufferLenMs", loadControl.getMaxPlayerBufferMs()), - field("minRebufferLenMs", loadControl.getMinBufferAfterReBufferMs()), - field("minSeekBufferLenMs", loadControl.getMinBufferAfterInteractionMs()) + @Override + public void newSession(final String sessionId, PlayerSettings playerSettings) { + + pkLog.e("New session " + sessionId); + + + if (PlayKitProfiler.this.sessionId != null) { + // close current session + closeSession(); + } + + PlayKitProfiler.this.sessionId = sessionId; + PlayKitProfiler.this.chunkCount = 0; + if (sessionId == null) { + return; // the null profiler + } + + PlayKitProfiler.this.sessionStartTime = SystemClock.elapsedRealtime(); + PlayKitProfiler.this.logQueue.clear(); + + PlayKitProfiler.this.serversLookedUp.clear(); + + pkLog.d("New profiler with sessionId: " + sessionId); + + log("StartSession", + field("now", System.currentTimeMillis()), + field("strNow", new Date().toString()), + field("sessionId", sessionId), + field("packageName", packageName) ); - } + log("PlayKit", + field("version", PlayKitManager.VERSION_STRING), + field("clientTag", PlayKitManager.CLIENT_TAG) + ); - logExperiments(); - } + log("Platform", + field("name", "Android"), + field("apiLevel", Build.VERSION.SDK_INT), + field("chipset", MediaSupport.DEVICE_CHIPSET), + field("brand", Build.BRAND), + field("model", Build.MODEL), + field("manufacturer", Build.MANUFACTURER), + field("device", Build.DEVICE), + field("tags", Build.TAGS), + field("fingerprint", Build.FINGERPRINT), + field("screenSize", metrics.widthPixels + "x" + metrics.heightPixels), + field("screenDpi", metrics.xdpi + "x" + metrics.ydpi), + field("deviceType", deviceType), + field("networkType", networkType) + ); - @Override - public AnalyticsListener getExoAnalyticsListener() { - return analyticsListener; - } + log("PlayerSettings", + field("allowClearLead", playerSettings.allowClearLead()), + field("useTextureView", playerSettings.useTextureView())); + + final LoadControlBuffers loadControl = playerSettings.getLoadControlBuffers(); + if (loadControl != null) { + log("PlayerLoadControl", + field("minBufferLenMs", loadControl.getMinPlayerBufferMs()), + field("maxBufferLenMs", loadControl.getMaxPlayerBufferMs()), + field("minRebufferLenMs", loadControl.getMinBufferAfterReBufferMs()), + field("minSeekBufferLenMs", loadControl.getMinBufferAfterInteractionMs()) + ); + } - @Override - public void onApplicationPaused() { - log("ApplicationPaused"); - } - @Override - public void onApplicationResumed() { - log("onApplicationResumed"); - } + logExperiments(); + } - @Override - public void onSetMedia(PKMediaConfig mediaConfig) { - JsonObject json = new JsonObject(); - json.add("entry", toJSON(mediaConfig.getMediaEntry())); - json.addProperty("startPosition", mediaConfig.getStartPosition()); + @Override + public AnalyticsListener getExoAnalyticsListener() { + return analyticsListener; + } - log("SetMedia", field("config", json.toString())); - } + @Override + public void onApplicationPaused() { + log("ApplicationPaused"); + } - @Override - public void onSeekRequested(long position) { - logWithPlaybackInfo("SeekRequested", timeField("targetPosition", position)); - } + @Override + public void onApplicationResumed() { + log("onApplicationResumed"); + } - @Override - public void onPauseRequested() { - logWithPlaybackInfo("PauseRequested"); - } + @Override + public void onSetMedia(PKMediaConfig mediaConfig) { + JsonObject json = new JsonObject(); + json.add("entry", toJSON(mediaConfig.getMediaEntry())); + json.addProperty("startPosition", mediaConfig.getStartPosition()); - @Override - public void onReplayRequested() { - logWithPlaybackInfo("ReplayRequested"); - } + log("SetMedia", field("config", json.toString())); + } - @Override - public void onPlayRequested() { - logWithPlaybackInfo("PlayRequested"); - } + @Override + public void onSeekRequested(long position) { + logWithPlaybackInfo("SeekRequested", timeField("targetPosition", position)); + } - @Override - public void onSessionFinished() { - closeSession(); - } + @Override + public void onPauseRequested() { + logWithPlaybackInfo("PauseRequested"); + } - @Override - public void onDurationChanged(long duration) { - log("DurationChanged", timeField("duration", duration)); - } + @Override + public void onReplayRequested() { + logWithPlaybackInfo("ReplayRequested"); + } - @Override - public EventListener.Factory getOkListenerFactory() { - return okListenerFactory; - } - }; + @Override + public void onPlayRequested() { + logWithPlaybackInfo("PlayRequested"); + } + + @Override + public void onSessionFinished() { + closeSession(); + } + + @Override + public void onDurationChanged(long duration) { + log("DurationChanged", timeField("duration", duration)); + } + + @Override + public EventListener.Factory getOkListenerFactory() { + return okListenerFactory; + } + }; + } private static class ConfigFile { - String putLogURL; + String postURL; float sendPercentage; } } From 0cce49221e3cbd6fec54d304ce4b43b9d7439c9b Mon Sep 17 00:00:00 2001 From: Noam Tamim Date: Mon, 25 May 2020 11:52:13 +0300 Subject: [PATCH 04/14] Profiler: log onIsPlayingChanged and ABRSettings --- .../playkit/profiler/ExoPlayerProfilingListener.java | 5 +++++ .../com/kaltura/playkit/profiler/PlayKitProfiler.java | 9 +++++++++ 2 files changed, 14 insertions(+) diff --git a/playkit/src/main/java/com/kaltura/playkit/profiler/ExoPlayerProfilingListener.java b/playkit/src/main/java/com/kaltura/playkit/profiler/ExoPlayerProfilingListener.java index 78677443b..e09fabde0 100644 --- a/playkit/src/main/java/com/kaltura/playkit/profiler/ExoPlayerProfilingListener.java +++ b/playkit/src/main/java/com/kaltura/playkit/profiler/ExoPlayerProfilingListener.java @@ -108,6 +108,11 @@ private String dataTypeString(int dataType) { } } + @Override + public void onIsPlayingChanged(EventTime eventTime, boolean isPlaying) { + log("IsPlayingChanged", field("isPlaying", isPlaying)); + } + @Override public void onPlayerStateChanged(EventTime eventTime, boolean playWhenReady, int playbackState) { String state; diff --git a/playkit/src/main/java/com/kaltura/playkit/profiler/PlayKitProfiler.java b/playkit/src/main/java/com/kaltura/playkit/profiler/PlayKitProfiler.java index 33e814563..d47922073 100644 --- a/playkit/src/main/java/com/kaltura/playkit/profiler/PlayKitProfiler.java +++ b/playkit/src/main/java/com/kaltura/playkit/profiler/PlayKitProfiler.java @@ -26,6 +26,7 @@ import com.kaltura.playkit.PKMediaSource; import com.kaltura.playkit.PlayKitManager; import com.kaltura.playkit.Utils; +import com.kaltura.playkit.player.ABRSettings; import com.kaltura.playkit.player.ExoPlayerWrapper; import com.kaltura.playkit.player.LoadControlBuffers; import com.kaltura.playkit.player.MediaSupport; @@ -626,6 +627,14 @@ public void newSession(final String sessionId, PlayerSettings playerSettings) { ); } + final ABRSettings abrSettings = playerSettings.getAbrSettings(); + if (abrSettings != null) { + log("PlayerAbrSettings", + field("initialBitrateEstimate", abrSettings.getInitialBitrateEstimate()), + field("minVideoBitrate", abrSettings.getMinVideoBitrate()), + field("maxVideoBitrate", abrSettings.getMaxVideoBitrate()) + ); + } logExperiments(); } From 467847c098e93244e0fd28afc97c8c31b2599077 Mon Sep 17 00:00:00 2001 From: Noam Tamim Date: Wed, 3 Jun 2020 22:51:21 +0300 Subject: [PATCH 05/14] Profiler: send logs as json --- .../main/java/com/kaltura/playkit/Utils.java | 65 +++- .../com/kaltura/playkit/player/Profiler.java | 18 + .../profiler/ExoPlayerProfilingListener.java | 154 ++++++--- .../playkit/profiler/OkHttpListener.java | 108 +++--- .../playkit/profiler/PlayKitProfiler.java | 307 ++++++------------ 5 files changed, 347 insertions(+), 305 deletions(-) diff --git a/playkit/src/main/java/com/kaltura/playkit/Utils.java b/playkit/src/main/java/com/kaltura/playkit/Utils.java index a08e53e05..a31d0a039 100644 --- a/playkit/src/main/java/com/kaltura/playkit/Utils.java +++ b/playkit/src/main/java/com/kaltura/playkit/Utils.java @@ -20,11 +20,13 @@ import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.Bundle; -import androidx.annotation.NonNull; - import android.telephony.TelephonyManager; import android.util.Base64; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.google.gson.JsonElement; import com.google.gson.JsonObject; import java.io.BufferedInputStream; @@ -293,4 +295,63 @@ public static String getDeviceType(Context context) { } return deviceType; } + + public static class JoBuilder { + private final JsonObject jo = new JsonObject(); + + public JsonObject jsonObject() { + return jo; + } + + public JoBuilder add(String key, String value) { + jo.addProperty(key, value); + return this; + } + + public JoBuilder add(String key, Number value) { + jo.addProperty(key, value); + return this; + } + + public JoBuilder add(String key, long value) { + jo.addProperty(key, value); + return this; + } + + public JoBuilder add(String key, float value) { + jo.addProperty(key, value); + return this; + } + + public JoBuilder add(String key, boolean value) { + jo.addProperty(key, value); + return this; + } + + public JoBuilder add(String key, Throwable value) { + jo.addProperty(key, "" + value); + return this; + } + + public JoBuilder add(String key, JsonElement value) { + jo.add(key, value); + return this; + } + + public JoBuilder addAll(@Nullable JsonObject otherJo) { + if (otherJo != null) { + for (Map.Entry entry : otherJo.entrySet()) { + jo.add(entry.getKey(), entry.getValue()); + } + } + return this; + } + + public JoBuilder addTime(String key, long millis) { + jo.addProperty(key, millis / 1000f); + return this; + } + + public void end() {} + } } diff --git a/playkit/src/main/java/com/kaltura/playkit/player/Profiler.java b/playkit/src/main/java/com/kaltura/playkit/player/Profiler.java index ab8510d74..f8979ad0a 100644 --- a/playkit/src/main/java/com/kaltura/playkit/player/Profiler.java +++ b/playkit/src/main/java/com/kaltura/playkit/player/Profiler.java @@ -4,6 +4,8 @@ import com.kaltura.android.exoplayer2.analytics.AnalyticsListener; import com.kaltura.playkit.PKMediaConfig; +import com.kaltura.playkit.Utils; +import com.kaltura.playkit.profiler.PlayKitProfiler; import okhttp3.EventListener; @@ -28,4 +30,20 @@ public void onDurationChanged(long duration) {/*NOOP*/} public void onApplicationPaused() {/*NOOP*/} public void onApplicationResumed() {/*NOOP*/} + + public static class Event extends Utils.JoBuilder { + + private final PlayKitProfiler profiler; + + public Event(PlayKitProfiler profiler, String name) { + this.profiler = profiler; + add("_ts", profiler.timestamp()); + add("_name", name); + } + + @Override + public void end() { + profiler.append(jsonObject()); + } + } } diff --git a/playkit/src/main/java/com/kaltura/playkit/profiler/ExoPlayerProfilingListener.java b/playkit/src/main/java/com/kaltura/playkit/profiler/ExoPlayerProfilingListener.java index e09fabde0..a2b59116f 100644 --- a/playkit/src/main/java/com/kaltura/playkit/profiler/ExoPlayerProfilingListener.java +++ b/playkit/src/main/java/com/kaltura/playkit/profiler/ExoPlayerProfilingListener.java @@ -20,6 +20,8 @@ import com.kaltura.android.exoplayer2.source.TrackGroupArray; import com.kaltura.android.exoplayer2.trackselection.TrackSelection; import com.kaltura.android.exoplayer2.trackselection.TrackSelectionArray; +import com.kaltura.playkit.Utils; +import com.kaltura.playkit.player.Profiler.Event; import java.io.IOException; import java.util.LinkedHashSet; @@ -45,12 +47,7 @@ import static com.kaltura.android.exoplayer2.Player.DISCONTINUITY_REASON_PERIOD_TRANSITION; import static com.kaltura.android.exoplayer2.Player.DISCONTINUITY_REASON_SEEK; import static com.kaltura.android.exoplayer2.Player.DISCONTINUITY_REASON_SEEK_ADJUSTMENT; - import static com.kaltura.playkit.profiler.PlayKitProfiler.MSEC_MULTIPLIER_FLOAT; -import static com.kaltura.playkit.profiler.PlayKitProfiler.field; -import static com.kaltura.playkit.profiler.PlayKitProfiler.joinFields; -import static com.kaltura.playkit.profiler.PlayKitProfiler.nullable; -import static com.kaltura.playkit.profiler.PlayKitProfiler.timeField; class ExoPlayerProfilingListener implements AnalyticsListener { @@ -61,8 +58,8 @@ class ExoPlayerProfilingListener implements AnalyticsListener { this.profiler = profiler; } - public void log(String event, String... strings) { - profiler.logWithPlaybackInfo(event, strings); + private Event log(String event) { + return profiler.logWithPlaybackInfo(event); } private String trackSelectionReasonString(int trackSelectionReason) { @@ -76,13 +73,17 @@ private String trackSelectionReasonString(int trackSelectionReason) { } } - private String trackFormatString(@Nullable Format trackFormat) { + private JsonObject trackFormatMap(@Nullable Format trackFormat) { if (trackFormat == null) { return null; } - return joinFields(nullable("id", trackFormat.id), "bitrate=" + trackFormat.bitrate, - nullable("codecs", trackFormat.codecs), nullable("language", trackFormat.language)); + return new Utils.JoBuilder() + .add("id", trackFormat.id) + .add("bitrate", trackFormat.bitrate) + .add("codecs", trackFormat.codecs) + .add("language", trackFormat.language) + .jsonObject(); } private String trackTypeString(int trackType) { @@ -110,7 +111,7 @@ private String dataTypeString(int dataType) { @Override public void onIsPlayingChanged(EventTime eventTime, boolean isPlaying) { - log("IsPlayingChanged", field("isPlaying", isPlaying)); + profiler.logWithPlaybackInfo("IsPlayingChanged").add("isPlaying", isPlaying).end(); } @Override @@ -132,12 +133,11 @@ public void onPlayerStateChanged(EventTime eventTime, boolean playWhenReady, int default: return; } - log("PlayerStateChanged", field("state", state), field("shouldPlay", playWhenReady)); + log("PlayerStateChanged").add("state", state).add("shouldPlay", playWhenReady).end(); } @Override public void onTimelineChanged(EventTime eventTime, int reason) { - } @Override @@ -148,37 +148,42 @@ public void onPositionDiscontinuity(EventTime eventTime, int reason) { reasonString = "PeriodTransition"; break; case DISCONTINUITY_REASON_SEEK: - reasonString = "PeriodTransition"; + reasonString = "Seek"; break; case DISCONTINUITY_REASON_SEEK_ADJUSTMENT: - reasonString = "PeriodTransition"; + reasonString = "SeekAdjustment"; break; case DISCONTINUITY_REASON_AD_INSERTION: - reasonString = "PeriodTransition"; + reasonString = "AdInsertion"; break; case DISCONTINUITY_REASON_INTERNAL: - reasonString = "PeriodTransition"; + reasonString = "Internal"; break; default: reasonString = "Unknown:" + reason; } - log("PositionDiscontinuity", field("reason", reasonString)); + log("PositionDiscontinuity") + .add("reason", reasonString) + .end(); } @Override public void onSeekStarted(EventTime eventTime) { - log("SeekStarted"); + log("SeekStarted").end(); } @Override public void onSeekProcessed(EventTime eventTime) { - log("SeekProcessed"); + log("SeekProcessed").end(); } @Override public void onPlaybackParametersChanged(EventTime eventTime, PlaybackParameters playbackParameters) { - log("PlaybackParametersChanged", field("speed", playbackParameters.speed), field("pitch", playbackParameters.pitch)); + log("PlaybackParametersChanged") + .add("speed", playbackParameters.speed) + .add("pitch", playbackParameters.pitch) + .end(); } @Override @@ -198,17 +203,23 @@ public void onRepeatModeChanged(EventTime eventTime, int repeatMode) { strMode = "Unknown(" + repeatMode + ")"; break; } - log("RepeatModeChanged", field("repeatMode", strMode)); + log("RepeatModeChanged") + .add("repeatMode", strMode) + .end(); } @Override public void onShuffleModeChanged(EventTime eventTime, boolean shuffleModeEnabled) { - log("ShuffleModeChanged", field("shuffleModeEnabled", shuffleModeEnabled)); + log("ShuffleModeChanged") + .add("shuffleModeEnabled", shuffleModeEnabled) + .end(); } @Override public void onLoadingChanged(EventTime eventTime, boolean isLoading) { - log("LoadingChanged", field("isLoading", isLoading)); + log("LoadingChanged") + .add("isLoading", isLoading) + .end(); } @Override @@ -232,7 +243,10 @@ public void onPlayerError(EventTime eventTime, ExoPlaybackException error) { break; } - log("PlayerError", field("type", type), "cause={" + error.getCause() + "}"); + log("PlayerError") + .add("type", type) + .add("cause", error.getCause()) + .end(); } @Override @@ -269,9 +283,10 @@ public void onTracksChanged(EventTime eventTime, TrackGroupArray trackGroups, Tr jTrackSelections.add(toJSON(selectedFormat)); } - log("TracksChanged", - field("available", jTrackGroups.toString()), - field("selected", jTrackSelections.toString())); + log("TracksChanged") + .add("available", jTrackGroups) + .add("selected", jTrackSelections) + .end(); } private JsonObject toJSON(@Nullable Format format) { @@ -293,7 +308,7 @@ private JsonObject toJSON(@Nullable Format format) { return jsonObject; } - private void logLoadingEvent(String event, MediaSourceEventListener.LoadEventInfo loadEventInfo, MediaSourceEventListener.MediaLoadData mediaLoadData, IOException error, Boolean wasCanceled) { + private void logLoadingEvent(String event, MediaSourceEventListener.LoadEventInfo loadEventInfo, MediaSourceEventListener.MediaLoadData mediaLoadData, @Nullable IOException error, @Nullable Boolean wasCanceled) { String dataTypeString = dataTypeString(mediaLoadData.dataType); String trackTypeString = trackTypeString(mediaLoadData.trackType); @@ -301,13 +316,24 @@ private void logLoadingEvent(String event, MediaSourceEventListener.LoadEventInf return; } - log(event, - timeField("time", loadEventInfo.elapsedRealtimeMs - profiler.sessionStartTime), field("uri", loadEventInfo.dataSpec.uri.toString()), - field("dataType", dataTypeString), field("trackType", trackTypeString), - trackFormatString(mediaLoadData.trackFormat), field("reason", trackSelectionReasonString(mediaLoadData.trackSelectionReason)), - timeField("rangeStart", mediaLoadData.mediaStartTimeMs), timeField("rangeEnd", mediaLoadData.mediaEndTimeMs), - timeField("loadTime", loadEventInfo.loadDurationMs), field("bytes", loadEventInfo.bytesLoaded), - field("error", error != null ? "{" + error.getMessage() + "}" : null), wasCanceled == null ? null : field("canceled", wasCanceled)); + final Utils.JoBuilder e = log(event) + .addTime("time", loadEventInfo.elapsedRealtimeMs - profiler.sessionStartTime) + .add("uri", loadEventInfo.dataSpec.uri.toString()) + .add("dataType", dataTypeString) + .add("trackType", trackTypeString) + .addAll(trackFormatMap(mediaLoadData.trackFormat)) + .add("reason", trackSelectionReasonString(mediaLoadData.trackSelectionReason)) + .addTime("rangeStart", mediaLoadData.mediaStartTimeMs) + .addTime("rangeEnd", mediaLoadData.mediaEndTimeMs) + .addTime("loadTime", loadEventInfo.loadDurationMs) + .add("bytes", loadEventInfo.bytesLoaded) + .add("error", error != null ? error.getMessage() : null); + + if (wasCanceled != null) { + e.add("canceled", wasCanceled); + } + + e.end(); } @Override @@ -338,7 +364,11 @@ public void onDownstreamFormatChanged(EventTime eventTime, MediaSourceEventListe return; } - log("DownstreamFormatChanged", field("trackType", trackTypeString), trackFormatString(mediaLoadData.trackFormat), field("reason", trackSelectionReasonString(mediaLoadData.trackSelectionReason))); + log("DownstreamFormatChanged") + .add("trackType", trackTypeString) + .addAll(trackFormatMap(mediaLoadData.trackFormat)) + .add("reason", trackSelectionReasonString(mediaLoadData.trackSelectionReason)) + .end(); } @Override @@ -347,7 +377,11 @@ public void onUpstreamDiscarded(EventTime eventTime, MediaSourceEventListener.Me if (trackTypeString == null) { return; } - log("UpstreamDiscarded", field("trackType", trackTypeString), field("start", mediaLoadData.mediaStartTimeMs / MSEC_MULTIPLIER_FLOAT), field("end", mediaLoadData.mediaEndTimeMs / MSEC_MULTIPLIER_FLOAT)); + log("UpstreamDiscarded") + .add("trackType", trackTypeString) + .add("start", mediaLoadData.mediaStartTimeMs / MSEC_MULTIPLIER_FLOAT) + .add("end", mediaLoadData.mediaEndTimeMs / MSEC_MULTIPLIER_FLOAT) + .end(); } @Override @@ -367,11 +401,11 @@ public void onReadingStarted(EventTime eventTime) { @Override public void onBandwidthEstimate(EventTime eventTime, int totalLoadTimeMs, long totalBytesLoaded, long bitrateEstimate) { - log("BandwidthSample", - field("bandwidth", bitrateEstimate), - timeField("totalLoadTime", totalLoadTimeMs), - field("totalBytesLoaded", totalBytesLoaded) - ); + log("BandwidthSample") + .add("bandwidth", bitrateEstimate) + .addTime("totalLoadTime", totalLoadTimeMs) + .add("totalBytesLoaded", totalBytesLoaded) + .end(); } @Override @@ -386,12 +420,19 @@ public void onDecoderEnabled(EventTime eventTime, int trackType, DecoderCounters @Override public void onDecoderInitialized(EventTime eventTime, int trackType, String decoderName, long initializationDurationMs) { - log("DecoderInitialized", field("name", decoderName), field("duration", initializationDurationMs / MSEC_MULTIPLIER_FLOAT)); + log("DecoderInitialized") + .add("name", decoderName) + .add("duration", initializationDurationMs / MSEC_MULTIPLIER_FLOAT) + .end(); } @Override public void onDecoderInputFormatChanged(EventTime eventTime, int trackType, Format format) { - log("DecoderInputFormatChanged", field("id", format.id), field("codecs", format.codecs), field("bitrate", format.bitrate)); + log("DecoderInputFormatChanged") + .add("id", format.id) + .add("codecs", format.codecs) + .add("bitrate", format.bitrate) + .end(); } @Override @@ -411,37 +452,46 @@ public void onAudioUnderrun(EventTime eventTime, int bufferSize, long bufferSize @Override public void onDroppedVideoFrames(EventTime eventTime, int droppedFrames, long elapsedMs) { - log("DroppedFrames", field("count", droppedFrames), field("time", elapsedMs / MSEC_MULTIPLIER_FLOAT)); + log("DroppedFrames") + .add("count", droppedFrames) + .add("time", elapsedMs / MSEC_MULTIPLIER_FLOAT) + .end(); } @Override public void onVideoSizeChanged(EventTime eventTime, int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) { - log("VideoSizeChanged", field("width", width), field("height", height)); + log("VideoSizeChanged") + .add("width", width) + .add("height", height) + .end(); } @Override public void onRenderedFirstFrame(EventTime eventTime, Surface surface) { - log("RenderedFirstFrame"); + log("RenderedFirstFrame").end(); } @Override public void onSurfaceSizeChanged(EventTime eventTime, int width, int height) { - log("ViewportSizeChange", field("width", width), field("height", height)); + log("ViewportSizeChange") + .add("width", width) + .add("height", height).end(); } @Override public void onVolumeChanged(EventTime eventTime, float volume) { - log("VolumeChanged", field("volume", volume)); + log("VolumeChanged") + .add("volume", volume).end(); } @Override public void onDrmSessionAcquired(EventTime eventTime) { - log("DrmSessionAcquired"); + log("DrmSessionAcquired").end(); } @Override public void onDrmSessionReleased(EventTime eventTime) { - log("DrmSessionReleased"); + log("DrmSessionReleased").end(); } @Override diff --git a/playkit/src/main/java/com/kaltura/playkit/profiler/OkHttpListener.java b/playkit/src/main/java/com/kaltura/playkit/profiler/OkHttpListener.java index cc617b02b..8d10828e7 100644 --- a/playkit/src/main/java/com/kaltura/playkit/profiler/OkHttpListener.java +++ b/playkit/src/main/java/com/kaltura/playkit/profiler/OkHttpListener.java @@ -2,9 +2,9 @@ import android.os.Build; import android.os.SystemClock; -import android.text.TextUtils; import com.kaltura.playkit.PKLog; +import com.kaltura.playkit.player.Profiler.Event; import java.io.IOException; import java.net.InetAddress; @@ -24,9 +24,6 @@ import okhttp3.Response; import okhttp3.TlsVersion; -import static com.kaltura.playkit.profiler.PlayKitProfiler.field; -import static com.kaltura.playkit.profiler.PlayKitProfiler.timeField; - class OkHttpListener extends EventListener { private static final PKLog log = PKLog.get("OkHttpListener"); @@ -42,23 +39,19 @@ class OkHttpListener extends EventListener { OkHttpListener(PlayKitProfiler playKitProfiler, Call call) { profiler = playKitProfiler; final Request request = call.request(); - if (request != null) { - HttpUrl httpUrl = request.url(); - if (httpUrl != null) { - hostName = httpUrl.host(); - url = httpUrl.toString(); - } else { - hostName = null; - url = null; - } - } else { - url = null; - hostName = null; - } + HttpUrl httpUrl = request.url(); + hostName = httpUrl.host(); + url = httpUrl.toString(); } - private void log(String event, String... strings) { - profiler.log("net_" + event, field("id", id), timeField("callTime", relTime()), TextUtils.join("\t", strings)); +// private void log(String event, String... strings) { +// profiler.log(event).add("id", id).addTime("callTime", relTime()), TextUtils.join("\t", strings)); +// } + + private Event logStart(String event) { + return (Event) profiler.logStart("net_" + event) + .add("id", id) + .addTime("callTime", relTime()); } private long relTime() { @@ -67,37 +60,39 @@ private long relTime() { @Override public void callStart(Call call) { - log.d("callStart " + id); - log("callStart", - field("url", url), field("hostName", hostName), - field("method", call.request().method())); + logStart("callStart") + .add("url", url) + .add("hostName", hostName) + .add("method", call.request().method()).end(); } @Override public void dnsStart(Call call, String domainName) { - log("dnsStart", field("hostName", domainName)); + logStart("dnsStart") + .add("hostName", domainName).end(); } @Override public void dnsEnd(Call call, String domainName, List inetAddressList) { if (!inetAddressList.isEmpty()) { final InetAddress address = inetAddressList.get(0); - log("dnsEnd", - field("hostName", domainName), - field("hostIp", address.getHostAddress()), - field("canonicalHostName", address.getCanonicalHostName())); + logStart("dnsEnd") + .add("hostName", domainName) + .add("hostIp", address.getHostAddress()) + .add("canonicalHostName", address.getCanonicalHostName()).end(); } else { - log("dnsEnd", field("hostName", domainName)); + logStart("dnsEnd") + .add("hostName", domainName).end(); } } @Override public void connectStart(Call call, InetSocketAddress inetSocketAddress, Proxy proxy) { - log("connectStart", - field("hostName", host(inetSocketAddress)), - field("hostIp", inetSocketAddress.getAddress().getHostAddress()), - field("port", inetSocketAddress.getPort()), - field("proxy", String.valueOf(proxy))); + logStart("connectStart") + .add("hostName", host(inetSocketAddress)) + .add("hostIp", inetSocketAddress.getAddress().getHostAddress()) + .add("port", inetSocketAddress.getPort()) + .add("proxy", String.valueOf(proxy)).end(); } private static String host(InetSocketAddress inetSocketAddress) { @@ -118,7 +113,7 @@ private static String host(Call call) { @Override public void secureConnectStart(Call call) { - log("secureConnectStart"); + logStart("secureConnectStart").end(); } @Override @@ -126,78 +121,87 @@ public void secureConnectEnd(Call call, Handshake handshake) { final CipherSuite cipherSuite = handshake.cipherSuite(); final TlsVersion tlsVersion = handshake.tlsVersion(); - log("secureConnectEnd", - field("cipherSuite", "" + cipherSuite), - field("tlsVersion", tlsVersion == null ? null : tlsVersion.javaName())); + logStart("secureConnectEnd") + .add("cipherSuite", "" + cipherSuite) + .add("tlsVersion", tlsVersion.javaName()) + .end(); } @Override public void connectEnd(Call call, InetSocketAddress inetSocketAddress, Proxy proxy, Protocol protocol) { - log("connectEnd", field("protocol", "" + protocol)); + logStart("connectEnd") + .add("protocol", "" + protocol) + .end(); } @Override public void connectFailed(Call call, InetSocketAddress inetSocketAddress, Proxy proxy, Protocol protocol, IOException ioe) { - log("connectFailed", field("error", ioe.toString())); + logStart("connectFailed") + .add("error", ioe.toString()) + .end(); } @Override public void connectionAcquired(Call call, Connection connection) { - log("connectionAcquired"); + logStart("connectionAcquired").end(); } @Override public void connectionReleased(Call call, Connection connection) { - log("connectionReleased"); + logStart("connectionReleased").end(); } @Override public void requestHeadersStart(Call call) { - log("requestHeadersStart"); + logStart("requestHeadersStart").end(); } @Override public void requestHeadersEnd(Call call, Request request) { - log("requestHeadersEnd"); + logStart("requestHeadersEnd").end(); } @Override public void requestBodyStart(Call call) { - log("requestBodyStart"); + logStart("requestBodyStart").end(); } @Override public void requestBodyEnd(Call call, long byteCount) { - log("requestBodyEnd"); + logStart("requestBodyEnd").end(); } @Override public void responseHeadersStart(Call call) { - log("responseHeadersStart"); + logStart("responseHeadersStart").end(); } @Override public void responseHeadersEnd(Call call, Response response) { - log("responseHeadersEnd"); + logStart("responseHeadersEnd").end(); } @Override public void responseBodyStart(Call call) { - log("responseBodyStart"); + logStart("responseBodyStart").end(); } @Override public void responseBodyEnd(Call call, long byteCount) { - log("responseBodyEnd", field("byteCount", byteCount)); + logStart("responseBodyEnd") + .add("byteCount", byteCount) + .end(); } @Override public void callEnd(Call call) { - log("callEnd"); + logStart("callEnd").end(); } @Override public void callFailed(Call call, IOException ioe) { - log("callFailed", field("error", ioe.toString())); + logStart("callFailed") + .add("error", ioe) + .end(); } } diff --git a/playkit/src/main/java/com/kaltura/playkit/profiler/PlayKitProfiler.java b/playkit/src/main/java/com/kaltura/playkit/profiler/PlayKitProfiler.java index d47922073..30710e3ae 100644 --- a/playkit/src/main/java/com/kaltura/playkit/profiler/PlayKitProfiler.java +++ b/playkit/src/main/java/com/kaltura/playkit/profiler/PlayKitProfiler.java @@ -8,16 +8,16 @@ import android.os.Looper; import android.os.Process; import android.os.SystemClock; -import android.text.TextUtils; import android.util.DisplayMetrics; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.google.gson.Gson; import com.google.gson.JsonArray; +import com.google.gson.JsonNull; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; -import com.kaltura.android.exoplayer2.C; import com.kaltura.android.exoplayer2.analytics.AnalyticsListener; import com.kaltura.playkit.PKDrmParams; import com.kaltura.playkit.PKLog; @@ -26,14 +26,13 @@ import com.kaltura.playkit.PKMediaSource; import com.kaltura.playkit.PlayKitManager; import com.kaltura.playkit.Utils; -import com.kaltura.playkit.player.ABRSettings; import com.kaltura.playkit.player.ExoPlayerWrapper; -import com.kaltura.playkit.player.LoadControlBuffers; import com.kaltura.playkit.player.MediaSupport; import com.kaltura.playkit.player.PKMediaSourceConfig; import com.kaltura.playkit.player.PlayerEngine; import com.kaltura.playkit.player.PlayerSettings; import com.kaltura.playkit.player.Profiler; +import com.kaltura.playkit.player.Profiler.Event; import com.kaltura.playkit.player.ProfilerFactory; import com.kaltura.playkit.utils.Consts; @@ -44,14 +43,9 @@ import java.lang.ref.WeakReference; import java.net.InetAddress; import java.net.UnknownHostException; -import java.util.ArrayList; import java.util.Date; import java.util.HashSet; import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentLinkedQueue; @@ -59,12 +53,14 @@ public class PlayKitProfiler { + public static final String FORMAT_VERSION = "2.0"; + // Static constants private static final PKLog pkLog = PKLog.get("PlayKitProfiler"); // Dev mode: shorter logs, write to local file, always enable - private static final boolean devMode = false; - private static final int SEND_INTERVAL_DEV = 60; // sec + private static final boolean devMode = true; + private static final int SEND_INTERVAL_DEV = 30; // sec private static final int SEND_PERCENTAGE_DEV = 100; // always private static final int SEND_INTERVAL_PROD = 60; // sec @@ -76,12 +72,10 @@ public class PlayKitProfiler { static final float MSEC_MULTIPLIER_FLOAT = 1000f; - private static final String SEPARATOR = "\t"; - - private static final Map experiments = new LinkedHashMap<>(); + private static final JsonObject experiments = new JsonObject(); private static final int PERCENTAGE_MULTIPLIER = 100; // Configuration - private static String postURL; + private static String postURL = "https://dtvqq1tbxf.execute-api.us-east-1.amazonaws.com/default/profilerLogCollector"; private static float sendPercentage = DEFAULT_SEND_PERCENTAGE; // Static setup private static Handler ioHandler; @@ -126,6 +120,7 @@ public void run() { * Initialize the static part of the profiler -- load the config and store it, * create IO thread and handler. Must be called by the app to enable the profiler. */ + @SuppressWarnings("unused") // Called by app public static void init(Context context, String configToken) { init(context, configToken, false); } @@ -196,38 +191,26 @@ public static void init(Context context, String configToken, boolean waitForConf * @param key A unique string that describes the property being tested * @param value the value of the key, must be a string, a number or a boolean. */ - public static void setExperiment(String key, Object value) { - if (key == null) { - pkLog.w("setExperiment: key is null"); - return; - } - - final String strValue; - if (value instanceof String) { - strValue = "{" + value + "}"; - } else if (value instanceof Number || value instanceof Boolean) { - strValue = value.toString(); + public static void setExperiment(@NonNull String key, @Nullable Object value) { + + if (value instanceof Number) { + experiments.addProperty(key, (Number) value); + } else if (value instanceof Boolean) { + experiments.addProperty(key, (Boolean) value); + } else if (value instanceof Character) { + experiments.addProperty(key, (Character) value); + } else if (value == null) { + experiments.add(key, JsonNull.INSTANCE); } else { - pkLog.w("setExperiment: value type is not valid (" + (value != null ? value.getClass().toString() : null) + "); ignored"); - return; + // Anything else (including a String) is used as a string + experiments.addProperty(key, value.toString()); } - - experiments.put(key, strValue); } private void logExperiments() { - - List values = new ArrayList<>(); - - for (Map.Entry entry : experiments.entrySet()) { - - final String key = entry.getKey(); - final String value = entry.getValue(); - - values.add(key + "=" + value); - } - - log("Experiments", TextUtils.join("\t", values)); + logStart("Experiments") + .addAll(experiments) + .end(); } private static void initMembers(final Context context) { @@ -238,19 +221,11 @@ private static void initMembers(final Context context) { networkType = Utils.getNetworkClass(context); deviceType = Utils.getDeviceType(context); - if (devMode) { externalFilesDir = context.getExternalFilesDir(null); } } - private static String toString(Enum e) { - if (e == null) { - return "null"; - } - return e.name(); - } - private static JsonObject toJSON(PKMediaEntry entry) { if (entry == null) { @@ -261,7 +236,7 @@ private static JsonObject toJSON(PKMediaEntry entry) { json.addProperty("id", entry.getId()); json.addProperty("duration", entry.getDuration()); - json.addProperty("type", toString(entry.getMediaType())); + json.addProperty("type", String.valueOf(entry.getMediaType())); if (entry.hasSources()) { JsonArray array = new JsonArray(); @@ -295,29 +270,6 @@ private static JsonObject toJSON(PKMediaSource source) { return json; } - static String field(String name, String value) { - if (value == null) { - return null; - } - return name + "={" + value + "}"; - } - - static String field(String name, long value) { - return name + "=" + value; - } - - static String field(String name, boolean value) { - return name + "=" + value; - } - - static String field(String name, float value) { - return String.format(Locale.US, "%s=%.03f", name, value); - } - - static String timeField(String name, long value) { - return value == C.TIME_UNSET ? field(name, null) : field(name, value / MSEC_MULTIPLIER_FLOAT); - } - private static void downloadConfig(String configToken) { final byte[] bytes; @@ -325,7 +277,7 @@ private static void downloadConfig(String configToken) { try { bytes = Utils.executeGet(CONFIG_BASE_URL + configToken + ".json", null); - if (bytes == null || bytes.length == 0) { + if (bytes.length == 0) { pkLog.w("Nothing returned from executeGet"); return; } @@ -340,26 +292,14 @@ private static void downloadConfig(String configToken) { private static void parseConfig(byte[] bytes) { try { final ConfigFile configFile = new Gson().fromJson(new String(bytes), ConfigFile.class); - postURL = configFile.postURL; - sendPercentage = configFile.sendPercentage; +// postURL = configFile.postURL; +// sendPercentage = configFile.sendPercentage; configLoaded = true; } catch (JsonParseException e) { pkLog.e("Failed to parse config", e); } } - static String nullable(String name, String value) { - if (value == null) { - return name + "=null"; - } - - return field(name, value); - } - - static String joinFields(String... fields) { - return TextUtils.join(SEPARATOR, fields); - } - private void sendLogChunk() { if (sessionId == null) { @@ -414,13 +354,14 @@ private void sendLogChunk() { } private void postChunk(String string, int chunkIndex) { + if (postURL == null) { pkLog.w("No POST URL"); return; } try { - final String url = postURL + "?mode=addChunk&sessionId=" + sessionId + "&index=" + chunkIndex; + final String url = postURL + "?sessionId=" + sessionId + "&index=" + chunkIndex; if (pkLog.isLoggable(PKLog.Level.verbose)) { pkLog.v("POST to " + url); pkLog.v(string); @@ -433,45 +374,21 @@ private void postChunk(String string, int chunkIndex) { } } - public void log(String event, String... strings) { - StringBuilder sb = startLog(event); - logPayload(sb, strings); - endLog(sb); + public long timestamp() { + return SystemClock.elapsedRealtime() - sessionStartTime; } - private StringBuilder startLog(String event) { - - // Pre-allocate the string to something reasonable - StringBuilder sb = new StringBuilder(100); - sb - .append(SystemClock.elapsedRealtime() - sessionStartTime) - .append(SEPARATOR) - .append(event); - - return sb; + public Event logStart(String name) { + return new Event(this, name); } - private void logPayload(StringBuilder sb, String... strings) { - for (String s : strings) { - if (s == null) { - continue; - } - sb.append(SEPARATOR).append(s); + public Event logStart(String name, @Nullable PlayerEngine playerEngine) { + final Event event = logStart(name); + if (playerEngine != null) { + event.addTime("pos", playerEngine.getCurrentPosition()); + event.addTime("buf", playerEngine.getBufferedPosition()); } - } - - private void endLog(final StringBuilder sb) { - logQueue.add(sb.toString()); - } - - private void logWithPlaybackInfo(String event, PlayerEngine playerEngine, String... strings) { - - StringBuilder sb = startLog(event); - - logPayload(sb, timeField("pos", playerEngine.getCurrentPosition()), timeField("buf", playerEngine.getBufferedPosition())); - logPayload(sb, strings); - - endLog(sb); + return event; } void maybeLogServerInfo(final Uri url) { @@ -504,13 +421,12 @@ private void logServerInfo(String hostName) { error = e.toString(); } - log("ServerInfo", - field("hostName", hostName), - field("canonicalHostName", canonicalHostName), - field("hostIp", hostIp), - timeField("lookupTime", lookupTime), - field("lookupError", error) - ); + logStart("ServerInfo") + .add("hostName", hostName) + .add("canonicalHostName", canonicalHostName) + .add("hostIp", hostIp) + .addTime("lookupTime", lookupTime) + .add("lookupError", error).end(); serversLookedUp.add(hostName); } @@ -520,12 +436,16 @@ private void closeSession() { sendLogChunk(); } - void logWithPlaybackInfo(String event, String... strings) { - if (playerEngine != null) { - logWithPlaybackInfo(event, playerEngine.get(), strings); - } + Event logWithPlaybackInfo(String event) { + return logStart(event, playerEngine != null ? playerEngine.get() : null); } +// void logWithPlaybackInfo(String event, String... strings) { +// if (playerEngine != null) { +// logWithPlaybackInfo(event, playerEngine.get(), strings); +// } +// } + private Profiler getProfilerImp() { if (profilerImp == null) { profilerImp = createProfilerImp(); @@ -554,9 +474,9 @@ public void onPrepareStarted(final PKMediaSourceConfig sourceConfig) { final Uri sourceUrl = sourceConfig.getRequestParams().url; - log("PrepareStarted", - field("engine", playerEngine.get().getClass().getSimpleName()), - field("source", sourceUrl.toString())); + logStart("PrepareStarted") + .add("engine", "ExoPlayer") + .add("source", sourceUrl.toString()).end(); maybeLogServerInfo(sourceUrl); } @@ -585,56 +505,37 @@ public void newSession(final String sessionId, PlayerSettings playerSettings) { pkLog.d("New profiler with sessionId: " + sessionId); - log("StartSession", - field("now", System.currentTimeMillis()), - field("strNow", new Date().toString()), - field("sessionId", sessionId), - field("packageName", packageName) - ); - - log("PlayKit", - field("version", PlayKitManager.VERSION_STRING), - field("clientTag", PlayKitManager.CLIENT_TAG) - ); - - log("Platform", - field("name", "Android"), - field("apiLevel", Build.VERSION.SDK_INT), - field("chipset", MediaSupport.DEVICE_CHIPSET), - field("brand", Build.BRAND), - field("model", Build.MODEL), - field("manufacturer", Build.MANUFACTURER), - field("device", Build.DEVICE), - field("tags", Build.TAGS), - field("fingerprint", Build.FINGERPRINT), - field("screenSize", metrics.widthPixels + "x" + metrics.heightPixels), - field("screenDpi", metrics.xdpi + "x" + metrics.ydpi), - field("deviceType", deviceType), - field("networkType", networkType) - ); - - log("PlayerSettings", - field("allowClearLead", playerSettings.allowClearLead()), - field("useTextureView", playerSettings.useTextureView())); - - final LoadControlBuffers loadControl = playerSettings.getLoadControlBuffers(); - if (loadControl != null) { - log("PlayerLoadControl", - field("minBufferLenMs", loadControl.getMinPlayerBufferMs()), - field("maxBufferLenMs", loadControl.getMaxPlayerBufferMs()), - field("minRebufferLenMs", loadControl.getMinBufferAfterReBufferMs()), - field("minSeekBufferLenMs", loadControl.getMinBufferAfterInteractionMs()) - ); - } - - final ABRSettings abrSettings = playerSettings.getAbrSettings(); - if (abrSettings != null) { - log("PlayerAbrSettings", - field("initialBitrateEstimate", abrSettings.getInitialBitrateEstimate()), - field("minVideoBitrate", abrSettings.getMinVideoBitrate()), - field("maxVideoBitrate", abrSettings.getMaxVideoBitrate()) - ); - } + logStart("StartSession") + .add("now", System.currentTimeMillis()) + .add("strNow", new Date().toString()) + .add("sessionId", sessionId) + .add("appId", packageName) + .add("version", FORMAT_VERSION) + .end(); + + logStart("PlayKit") + .add("version", PlayKitManager.VERSION_STRING) + .add("clientTag", PlayKitManager.CLIENT_TAG) + .add("type", "sdk").end(); + + logStart("Platform") + .add("os", "Android") + .add("screenSize", metrics.widthPixels + "x" + metrics.heightPixels) + .add("screenDpi", metrics.xdpi + "x" + metrics.ydpi) + .add("deviceType", deviceType) + .add("networkType", networkType).end(); + + logStart("AndroidInfo") + .add("apiLevel", Build.VERSION.SDK_INT) + .add("chipset", MediaSupport.DEVICE_CHIPSET) + .add("brand", Build.BRAND) + .add("model", Build.MODEL) + .add("manufacturer", Build.MANUFACTURER) + .add("device", Build.DEVICE) + .add("tags", Build.TAGS) + .add("fingerprint", Build.FINGERPRINT).end(); + + logPlayerSettings(); logExperiments(); } @@ -646,41 +547,40 @@ public AnalyticsListener getExoAnalyticsListener() { @Override public void onApplicationPaused() { - log("ApplicationPaused"); + logStart("ApplicationPaused").end(); } @Override public void onApplicationResumed() { - log("onApplicationResumed"); + logStart("ApplicationResumed").end(); } @Override public void onSetMedia(PKMediaConfig mediaConfig) { - JsonObject json = new JsonObject(); - json.add("entry", toJSON(mediaConfig.getMediaEntry())); - json.addProperty("startPosition", mediaConfig.getStartPosition()); - log("SetMedia", field("config", json.toString())); + logStart("SetMedia") + .add("entry", toJSON(mediaConfig.getMediaEntry())) + .add("startPosition", mediaConfig.getStartPosition()).end(); } @Override public void onSeekRequested(long position) { - logWithPlaybackInfo("SeekRequested", timeField("targetPosition", position)); + logWithPlaybackInfo("SeekRequested").addTime("targetPosition", position).end(); } @Override public void onPauseRequested() { - logWithPlaybackInfo("PauseRequested"); + logWithPlaybackInfo("PauseRequested").end(); } @Override public void onReplayRequested() { - logWithPlaybackInfo("ReplayRequested"); + logWithPlaybackInfo("ReplayRequested").end(); } @Override public void onPlayRequested() { - logWithPlaybackInfo("PlayRequested"); + logWithPlaybackInfo("PlayRequested").end(); } @Override @@ -690,7 +590,7 @@ public void onSessionFinished() { @Override public void onDurationChanged(long duration) { - log("DurationChanged", timeField("duration", duration)); + logStart("DurationChanged").addTime("duration", duration).end(); } @Override @@ -700,6 +600,15 @@ public EventListener.Factory getOkListenerFactory() { }; } + private void logPlayerSettings() { + // TODO: 27/05/2020 + logStart("PlayerSettings").end(); + } + + public void append(JsonObject jo) { + logQueue.add(jo.toString()); + } + private static class ConfigFile { String postURL; float sendPercentage; From 4826d8135e415613a9b22888777f23535753f5a3 Mon Sep 17 00:00:00 2001 From: Noam Tamim Date: Sat, 6 Jun 2020 22:34:32 +0300 Subject: [PATCH 06/14] Don't log LoadStarted event - it's not used LoadCompleted/Cancelled/Failed contain all info. --- .../kaltura/playkit/profiler/ExoPlayerProfilingListener.java | 2 +- .../java/com/kaltura/playkit/profiler/PlayKitProfiler.java | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/playkit/src/main/java/com/kaltura/playkit/profiler/ExoPlayerProfilingListener.java b/playkit/src/main/java/com/kaltura/playkit/profiler/ExoPlayerProfilingListener.java index a2b59116f..f2231908a 100644 --- a/playkit/src/main/java/com/kaltura/playkit/profiler/ExoPlayerProfilingListener.java +++ b/playkit/src/main/java/com/kaltura/playkit/profiler/ExoPlayerProfilingListener.java @@ -338,7 +338,7 @@ private void logLoadingEvent(String event, MediaSourceEventListener.LoadEventInf @Override public void onLoadStarted(EventTime eventTime, MediaSourceEventListener.LoadEventInfo loadEventInfo, MediaSourceEventListener.MediaLoadData mediaLoadData) { - logLoadingEvent("LoadStarted", loadEventInfo, mediaLoadData, null, null); +// logLoadingEvent("LoadStarted", loadEventInfo, mediaLoadData, null, null); profiler.maybeLogServerInfo(loadEventInfo.uri); } diff --git a/playkit/src/main/java/com/kaltura/playkit/profiler/PlayKitProfiler.java b/playkit/src/main/java/com/kaltura/playkit/profiler/PlayKitProfiler.java index 30710e3ae..c9f5e01aa 100644 --- a/playkit/src/main/java/com/kaltura/playkit/profiler/PlayKitProfiler.java +++ b/playkit/src/main/java/com/kaltura/playkit/profiler/PlayKitProfiler.java @@ -484,9 +484,8 @@ public void onPrepareStarted(final PKMediaSourceConfig sourceConfig) { @Override public void newSession(final String sessionId, PlayerSettings playerSettings) { - pkLog.e("New session " + sessionId); - - + pkLog.d("New session " + sessionId); + if (PlayKitProfiler.this.sessionId != null) { // close current session closeSession(); From 4d117591c3493d0743bb7081f2da46db200983b4 Mon Sep 17 00:00:00 2001 From: Noam Tamim Date: Sun, 7 Jun 2020 11:48:15 +0300 Subject: [PATCH 07/14] Profiler is configured by PlayKitManager, not app --- .../com/kaltura/playkit/PlayKitManager.java | 16 +++-- .../playkit/profiler/PlayKitProfiler.java | 61 +++---------------- 2 files changed, 18 insertions(+), 59 deletions(-) diff --git a/playkit/src/main/java/com/kaltura/playkit/PlayKitManager.java b/playkit/src/main/java/com/kaltura/playkit/PlayKitManager.java index 75dc01850..e7493d86a 100644 --- a/playkit/src/main/java/com/kaltura/playkit/PlayKitManager.java +++ b/playkit/src/main/java/com/kaltura/playkit/PlayKitManager.java @@ -15,7 +15,9 @@ import android.content.Context; import androidx.annotation.Nullable; +import com.google.gson.Gson; import com.kaltura.playkit.player.MediaSupport; +import com.kaltura.playkit.profiler.PlayKitProfiler; import java.util.HashMap; import java.util.Map; @@ -56,10 +58,11 @@ static PKPlugin createPlugin(String name) { public static Player loadPlayer(Context context, @Nullable PKPluginConfigs pluginConfigs, MessageBus messageBus) { MediaSupport.initializeDrm(context, null); + Gson gson = new Gson(); + final ProfilerConfig profilerConfig = gson.fromJson("{\"postURL\": \"https://dtvqq1tbxf.execute-api.us-east-1.amazonaws.com/default/profilerLogCollector\", \"sendPercentage\": 100}", ProfilerConfig.class); + PlayKitProfiler.init(context, profilerConfig); - if (shouldSendDeviceCapabilitiesReport) { - PKDeviceCapabilities.maybeSendReport(context); - } + PKDeviceCapabilities.maybeSendReport(context); PlayerLoader playerLoader = new PlayerLoader(context, messageBus); playerLoader.load(pluginConfigs != null ? pluginConfigs : new PKPluginConfigs()); @@ -67,13 +70,14 @@ public static Player loadPlayer(Context context, @Nullable PKPluginConfigs plugi } public static Player loadPlayer(Context context, @Nullable PKPluginConfigs pluginConfigs) { - return loadPlayer(context, pluginConfigs, null); } + public static final class ProfilerConfig { + public String postURL; + public float sendPercentage; - public static void disableDeviceCapabilitiesReport() { - shouldSendDeviceCapabilitiesReport = false; + private ProfilerConfig() {} } } diff --git a/playkit/src/main/java/com/kaltura/playkit/profiler/PlayKitProfiler.java b/playkit/src/main/java/com/kaltura/playkit/profiler/PlayKitProfiler.java index c9f5e01aa..630db5433 100644 --- a/playkit/src/main/java/com/kaltura/playkit/profiler/PlayKitProfiler.java +++ b/playkit/src/main/java/com/kaltura/playkit/profiler/PlayKitProfiler.java @@ -13,11 +13,9 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonNull; import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; import com.kaltura.android.exoplayer2.analytics.AnalyticsListener; import com.kaltura.playkit.PKDrmParams; import com.kaltura.playkit.PKLog; @@ -25,6 +23,7 @@ import com.kaltura.playkit.PKMediaEntry; import com.kaltura.playkit.PKMediaSource; import com.kaltura.playkit.PlayKitManager; +import com.kaltura.playkit.PlayKitManager.ProfilerConfig; import com.kaltura.playkit.Utils; import com.kaltura.playkit.player.ExoPlayerWrapper; import com.kaltura.playkit.player.MediaSupport; @@ -120,12 +119,7 @@ public void run() { * Initialize the static part of the profiler -- load the config and store it, * create IO thread and handler. Must be called by the app to enable the profiler. */ - @SuppressWarnings("unused") // Called by app - public static void init(Context context, String configToken) { - init(context, configToken, false); - } - - public static void init(Context context, String configToken, boolean waitForConfig) { + public static void init(Context context, ProfilerConfig config) { // This only has to happen once. if (initialized) { @@ -145,7 +139,7 @@ public static void init(Context context, String configToken, boolean waitForConf handlerThread.start(); ioHandler = new Handler(handlerThread.getLooper()); - ioHandler.post(() -> downloadConfig(configToken)); + ioHandler.post(() -> applyConfig(config)); initMembers(appContext); @@ -170,16 +164,6 @@ public static void init(Context context, String configToken, boolean waitForConf } return null; }); - - if (waitForConfig) { - while (!configLoaded) { - try { - Thread.sleep(100); - } catch (InterruptedException e) { - pkLog.d("waitForConfig interrupted"); - } - } - } } } @@ -270,34 +254,10 @@ private static JsonObject toJSON(PKMediaSource source) { return json; } - private static void downloadConfig(String configToken) { - final byte[] bytes; - - // Download - try { - bytes = Utils.executeGet(CONFIG_BASE_URL + configToken + ".json", null); - - if (bytes.length == 0) { - pkLog.w("Nothing returned from executeGet"); - return; - } - - parseConfig(bytes); - - } catch (IOException e) { - pkLog.w("Failed to download config", e); - } - } - - private static void parseConfig(byte[] bytes) { - try { - final ConfigFile configFile = new Gson().fromJson(new String(bytes), ConfigFile.class); -// postURL = configFile.postURL; -// sendPercentage = configFile.sendPercentage; - configLoaded = true; - } catch (JsonParseException e) { - pkLog.e("Failed to parse config", e); - } + private static void applyConfig(ProfilerConfig config) { + postURL = config.postURL; + sendPercentage = config.sendPercentage; + configLoaded = true; } private void sendLogChunk() { @@ -485,7 +445,7 @@ public void onPrepareStarted(final PKMediaSourceConfig sourceConfig) { public void newSession(final String sessionId, PlayerSettings playerSettings) { pkLog.d("New session " + sessionId); - + if (PlayKitProfiler.this.sessionId != null) { // close current session closeSession(); @@ -607,9 +567,4 @@ private void logPlayerSettings() { public void append(JsonObject jo) { logQueue.add(jo.toString()); } - - private static class ConfigFile { - String postURL; - float sendPercentage; - } } From cf924014f8d061ba06a149dc9f79b85acd9ef011 Mon Sep 17 00:00:00 2001 From: Noam Tamim Date: Sun, 7 Jun 2020 17:17:21 +0300 Subject: [PATCH 08/14] Profiler: log player settings --- .../com/kaltura/playkit/PlayKitManager.java | 12 +- .../main/java/com/kaltura/playkit/Utils.java | 35 +++- .../com/kaltura/playkit/player/Profiler.java | 2 +- .../profiler/ExoPlayerProfilingListener.java | 4 +- .../playkit/profiler/PlayKitProfiler.java | 92 ++------- .../com/kaltura/playkit/profiler/ToJson.java | 185 ++++++++++++++++++ 6 files changed, 236 insertions(+), 94 deletions(-) create mode 100644 playkit/src/main/java/com/kaltura/playkit/profiler/ToJson.java diff --git a/playkit/src/main/java/com/kaltura/playkit/PlayKitManager.java b/playkit/src/main/java/com/kaltura/playkit/PlayKitManager.java index e7493d86a..702dfdd6a 100644 --- a/playkit/src/main/java/com/kaltura/playkit/PlayKitManager.java +++ b/playkit/src/main/java/com/kaltura/playkit/PlayKitManager.java @@ -58,9 +58,7 @@ static PKPlugin createPlugin(String name) { public static Player loadPlayer(Context context, @Nullable PKPluginConfigs pluginConfigs, MessageBus messageBus) { MediaSupport.initializeDrm(context, null); - Gson gson = new Gson(); - final ProfilerConfig profilerConfig = gson.fromJson("{\"postURL\": \"https://dtvqq1tbxf.execute-api.us-east-1.amazonaws.com/default/profilerLogCollector\", \"sendPercentage\": 100}", ProfilerConfig.class); - PlayKitProfiler.init(context, profilerConfig); + initializeProfiler(context); PKDeviceCapabilities.maybeSendReport(context); @@ -69,6 +67,14 @@ public static Player loadPlayer(Context context, @Nullable PKPluginConfigs plugi return playerLoader; } + private static void initializeProfiler(Context context) { + Gson gson = new Gson(); + // TODO: 07/06/2020 This should come from the backend + final String json = "{\"postURL\": \"https://dtvqq1tbxf.execute-api.us-east-1.amazonaws.com/default/profilerLogCollector\", \"sendPercentage\": 100}"; + final ProfilerConfig profilerConfig = gson.fromJson(json, ProfilerConfig.class); + PlayKitProfiler.init(context, profilerConfig); + } + public static Player loadPlayer(Context context, @Nullable PKPluginConfigs pluginConfigs) { return loadPlayer(context, pluginConfigs, null); } diff --git a/playkit/src/main/java/com/kaltura/playkit/Utils.java b/playkit/src/main/java/com/kaltura/playkit/Utils.java index a31d0a039..6bc50a41d 100644 --- a/playkit/src/main/java/com/kaltura/playkit/Utils.java +++ b/playkit/src/main/java/com/kaltura/playkit/Utils.java @@ -27,6 +27,7 @@ import androidx.annotation.Nullable; import com.google.gson.JsonElement; +import com.google.gson.JsonNull; import com.google.gson.JsonObject; import java.io.BufferedInputStream; @@ -296,49 +297,54 @@ public static String getDeviceType(Context context) { return deviceType; } - public static class JoBuilder { + public static class GsonObject { private final JsonObject jo = new JsonObject(); public JsonObject jsonObject() { return jo; } - public JoBuilder add(String key, String value) { + public GsonObject add(String key, String value) { jo.addProperty(key, value); return this; } - public JoBuilder add(String key, Number value) { + public GsonObject add(String key, Number value) { jo.addProperty(key, value); return this; } - public JoBuilder add(String key, long value) { + public GsonObject add(String key, long value) { jo.addProperty(key, value); return this; } - public JoBuilder add(String key, float value) { + public GsonObject add(String key, float value) { jo.addProperty(key, value); return this; } - public JoBuilder add(String key, boolean value) { + public GsonObject add(String key, boolean value) { jo.addProperty(key, value); return this; } - public JoBuilder add(String key, Throwable value) { + public GsonObject add(String key, Throwable value) { jo.addProperty(key, "" + value); return this; } - public JoBuilder add(String key, JsonElement value) { + public GsonObject add(String key, JsonElement value) { jo.add(key, value); return this; } - public JoBuilder addAll(@Nullable JsonObject otherJo) { + public GsonObject add(String key, @Nullable GsonObject value) { + jo.add(key, value != null ? value.jo : null); + return this; + } + + public GsonObject addAll(@Nullable JsonObject otherJo) { if (otherJo != null) { for (Map.Entry entry : otherJo.entrySet()) { jo.add(entry.getKey(), entry.getValue()); @@ -347,7 +353,16 @@ public JoBuilder addAll(@Nullable JsonObject otherJo) { return this; } - public JoBuilder addTime(String key, long millis) { + public GsonObject addAll(@Nullable GsonObject otherGo) { + if (otherGo != null) { + for (Map.Entry entry : otherGo.jo.entrySet()) { + jo.add(entry.getKey(), entry.getValue()); + } + } + return this; + } + + public GsonObject addTime(String key, long millis) { jo.addProperty(key, millis / 1000f); return this; } diff --git a/playkit/src/main/java/com/kaltura/playkit/player/Profiler.java b/playkit/src/main/java/com/kaltura/playkit/player/Profiler.java index f8979ad0a..984bbd2d7 100644 --- a/playkit/src/main/java/com/kaltura/playkit/player/Profiler.java +++ b/playkit/src/main/java/com/kaltura/playkit/player/Profiler.java @@ -31,7 +31,7 @@ public void onDurationChanged(long duration) {/*NOOP*/} public void onApplicationPaused() {/*NOOP*/} public void onApplicationResumed() {/*NOOP*/} - public static class Event extends Utils.JoBuilder { + public static class Event extends Utils.GsonObject { private final PlayKitProfiler profiler; diff --git a/playkit/src/main/java/com/kaltura/playkit/profiler/ExoPlayerProfilingListener.java b/playkit/src/main/java/com/kaltura/playkit/profiler/ExoPlayerProfilingListener.java index f2231908a..b430ca997 100644 --- a/playkit/src/main/java/com/kaltura/playkit/profiler/ExoPlayerProfilingListener.java +++ b/playkit/src/main/java/com/kaltura/playkit/profiler/ExoPlayerProfilingListener.java @@ -78,7 +78,7 @@ private JsonObject trackFormatMap(@Nullable Format trackFormat) { return null; } - return new Utils.JoBuilder() + return new Utils.GsonObject() .add("id", trackFormat.id) .add("bitrate", trackFormat.bitrate) .add("codecs", trackFormat.codecs) @@ -316,7 +316,7 @@ private void logLoadingEvent(String event, MediaSourceEventListener.LoadEventInf return; } - final Utils.JoBuilder e = log(event) + final Utils.GsonObject e = log(event) .addTime("time", loadEventInfo.elapsedRealtimeMs - profiler.sessionStartTime) .add("uri", loadEventInfo.dataSpec.uri.toString()) .add("dataType", dataTypeString) diff --git a/playkit/src/main/java/com/kaltura/playkit/profiler/PlayKitProfiler.java b/playkit/src/main/java/com/kaltura/playkit/profiler/PlayKitProfiler.java index 630db5433..6571c9f97 100644 --- a/playkit/src/main/java/com/kaltura/playkit/profiler/PlayKitProfiler.java +++ b/playkit/src/main/java/com/kaltura/playkit/profiler/PlayKitProfiler.java @@ -2,7 +2,6 @@ import android.content.Context; import android.net.Uri; -import android.os.Build; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; @@ -13,20 +12,15 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.google.gson.JsonArray; import com.google.gson.JsonNull; import com.google.gson.JsonObject; import com.kaltura.android.exoplayer2.analytics.AnalyticsListener; -import com.kaltura.playkit.PKDrmParams; import com.kaltura.playkit.PKLog; import com.kaltura.playkit.PKMediaConfig; -import com.kaltura.playkit.PKMediaEntry; -import com.kaltura.playkit.PKMediaSource; import com.kaltura.playkit.PlayKitManager; import com.kaltura.playkit.PlayKitManager.ProfilerConfig; import com.kaltura.playkit.Utils; import com.kaltura.playkit.player.ExoPlayerWrapper; -import com.kaltura.playkit.player.MediaSupport; import com.kaltura.playkit.player.PKMediaSourceConfig; import com.kaltura.playkit.player.PlayerEngine; import com.kaltura.playkit.player.PlayerSettings; @@ -67,8 +61,6 @@ public class PlayKitProfiler { private static final float DEFAULT_SEND_PERCENTAGE = devMode ? SEND_PERCENTAGE_DEV : 0; // Start disabled - private static final String CONFIG_BASE_URL = "https://s3.amazonaws.com/player-profiler/configs/"; - static final float MSEC_MULTIPLIER_FLOAT = 1000f; private static final JsonObject experiments = new JsonObject(); @@ -84,7 +76,6 @@ public class PlayKitProfiler { private static String packageName; private static String networkType; private static String deviceType; - private static boolean configLoaded; private final ConcurrentLinkedQueue logQueue = new ConcurrentLinkedQueue<>(); private final ExoPlayerProfilingListener analyticsListener = new ExoPlayerProfilingListener(this); private final EventListener.Factory okListenerFactory = call -> new OkHttpListener(PlayKitProfiler.this, call); @@ -210,54 +201,9 @@ private static void initMembers(final Context context) { } } - private static JsonObject toJSON(PKMediaEntry entry) { - - if (entry == null) { - return null; - } - - JsonObject json = new JsonObject(); - - json.addProperty("id", entry.getId()); - json.addProperty("duration", entry.getDuration()); - json.addProperty("type", String.valueOf(entry.getMediaType())); - - if (entry.hasSources()) { - JsonArray array = new JsonArray(); - for (PKMediaSource source : entry.getSources()) { - array.add(toJSON(source)); - } - json.add("sources", array); - } - - return json; - } - - private static JsonObject toJSON(PKMediaSource source) { - JsonObject json = new JsonObject(); - - json.addProperty("id", source.getId()); - json.addProperty("format", source.getMediaFormat().name()); - json.addProperty("url", source.getUrl()); - - if (source.hasDrmParams()) { - JsonArray array = new JsonArray(); - for (PKDrmParams params : source.getDrmData()) { - PKDrmParams.Scheme scheme = params.getScheme(); - if (scheme != null) { - array.add(scheme.name()); - } - } - json.add("drm", array); - } - - return json; - } - private static void applyConfig(ProfilerConfig config) { postURL = config.postURL; sendPercentage = config.sendPercentage; - configLoaded = true; } private void sendLogChunk() { @@ -400,12 +346,6 @@ Event logWithPlaybackInfo(String event) { return logStart(event, playerEngine != null ? playerEngine.get() : null); } -// void logWithPlaybackInfo(String event, String... strings) { -// if (playerEngine != null) { -// logWithPlaybackInfo(event, playerEngine.get(), strings); -// } -// } - private Profiler getProfilerImp() { if (profilerImp == null) { profilerImp = createProfilerImp(); @@ -485,16 +425,12 @@ public void newSession(final String sessionId, PlayerSettings playerSettings) { .add("networkType", networkType).end(); logStart("AndroidInfo") - .add("apiLevel", Build.VERSION.SDK_INT) - .add("chipset", MediaSupport.DEVICE_CHIPSET) - .add("brand", Build.BRAND) - .add("model", Build.MODEL) - .add("manufacturer", Build.MANUFACTURER) - .add("device", Build.DEVICE) - .add("tags", Build.TAGS) - .add("fingerprint", Build.FINGERPRINT).end(); + .addAll(ToJson.buildInfoJson()) + .end(); - logPlayerSettings(); + logStart("PlayerSettings") + .addAll(ToJson.toJson(playerSettings)) + .end(); logExperiments(); } @@ -518,13 +454,16 @@ public void onApplicationResumed() { public void onSetMedia(PKMediaConfig mediaConfig) { logStart("SetMedia") - .add("entry", toJSON(mediaConfig.getMediaEntry())) - .add("startPosition", mediaConfig.getStartPosition()).end(); + .add("entry", ToJson.toJson(mediaConfig.getMediaEntry())) + .add("startPosition", mediaConfig.getStartPosition()) + .end(); } @Override public void onSeekRequested(long position) { - logWithPlaybackInfo("SeekRequested").addTime("targetPosition", position).end(); + logWithPlaybackInfo("SeekRequested") + .addTime("targetPosition", position) + .end(); } @Override @@ -549,7 +488,9 @@ public void onSessionFinished() { @Override public void onDurationChanged(long duration) { - logStart("DurationChanged").addTime("duration", duration).end(); + logStart("DurationChanged") + .addTime("duration", duration) + .end(); } @Override @@ -559,11 +500,6 @@ public EventListener.Factory getOkListenerFactory() { }; } - private void logPlayerSettings() { - // TODO: 27/05/2020 - logStart("PlayerSettings").end(); - } - public void append(JsonObject jo) { logQueue.add(jo.toString()); } diff --git a/playkit/src/main/java/com/kaltura/playkit/profiler/ToJson.java b/playkit/src/main/java/com/kaltura/playkit/profiler/ToJson.java new file mode 100644 index 000000000..b43845e25 --- /dev/null +++ b/playkit/src/main/java/com/kaltura/playkit/profiler/ToJson.java @@ -0,0 +1,185 @@ +package com.kaltura.playkit.profiler; + +import android.graphics.Typeface; +import android.os.Build; + +import com.google.gson.JsonArray; +import com.kaltura.android.exoplayer2.text.CaptionStyleCompat; +import com.kaltura.playkit.PKDrmParams; +import com.kaltura.playkit.PKMediaEntry; +import com.kaltura.playkit.PKMediaSource; +import com.kaltura.playkit.PKTrackConfig; +import com.kaltura.playkit.Utils.GsonObject; +import com.kaltura.playkit.player.ABRSettings; +import com.kaltura.playkit.player.LoadControlBuffers; +import com.kaltura.playkit.player.MediaSupport; +import com.kaltura.playkit.player.PKMaxVideoSize; +import com.kaltura.playkit.player.PKSubtitlePosition; +import com.kaltura.playkit.player.PlayerSettings; +import com.kaltura.playkit.player.SubtitleStyleSettings; +import com.kaltura.playkit.player.vr.VRSettings; + +public class ToJson { + static GsonObject toJson(PKMediaEntry entry) { + + if (entry == null) { + return null; + } + + JsonArray sources = new JsonArray(); + if (entry.hasSources()) { + for (PKMediaSource source : entry.getSources()) { + sources.add(toJson(source).jsonObject()); + } + } + + return new GsonObject() + .add("id", entry.getId()) + .add("duration", entry.getDuration()) + .add("type", String.valueOf(entry.getMediaType())) + .add("sources", sources); + } + + static GsonObject toJson(PKMediaSource source) { + JsonArray drmParams = new JsonArray(); + if (source.hasDrmParams()) { + for (PKDrmParams params : source.getDrmData()) { + PKDrmParams.Scheme scheme = params.getScheme(); + if (scheme != null) { + drmParams.add(scheme.name()); + } + } + } + + return new GsonObject() + .add("id", source.getId()) + .add("format", source.getMediaFormat().name()) + .add("url", source.getUrl()) + .add("drm", drmParams); + } + + static GsonObject toJson(ABRSettings s) { + return new GsonObject() + .add("initialBitrateEstimate", s.getInitialBitrateEstimate()) + .add("maxVideoBitrate", s.getMaxVideoBitrate()) + .add("minVideoBitrate", s.getMinVideoBitrate()); + } + + static GsonObject toJson(VRSettings s) { + return new GsonObject() + .add("flingEnabled", s.isFlingEnabled()) + .add("vrModeEnabled", s.isVrModeEnabled()) + .add("zoomWithPinchEnabled", s.isZoomWithPinchEnabled()) + .add("interactionMode", s.getInteractionMode().name()); + } + + static GsonObject toJson(SubtitleStyleSettings s) { + return new GsonObject() + .add("textColor", Integer.toHexString(s.getTextColor())) + .add("backgroundColor", Integer.toHexString(s.getBackgroundColor())) + .add("textSizeFraction", s.getTextSizeFraction()) + .add("windowColor", Integer.toHexString(s.getWindowColor())) + .add("edgeType", edgeTypeName(s.getEdgeType())) + .add("edgeColor", Integer.toHexString(s.getEdgeColor())) + .add("typeface", toJson(s.getTypeface())) + .add("styleName", s.getStyleName()) + .add("position", toJson(s.getSubtitlePosition())); + } + + private static String edgeTypeName(@CaptionStyleCompat.EdgeType int edgeType) { + switch (edgeType) { + case CaptionStyleCompat.EDGE_TYPE_NONE: + return "none"; + case CaptionStyleCompat.EDGE_TYPE_OUTLINE: + return "outline"; + case CaptionStyleCompat.EDGE_TYPE_DROP_SHADOW: + return "dropShadow"; + case CaptionStyleCompat.EDGE_TYPE_RAISED: + return "raised"; + case CaptionStyleCompat.EDGE_TYPE_DEPRESSED: + return "depressed"; + default: + return "unknown"; + } + } + + static GsonObject toJson(PKSubtitlePosition s) { + return new GsonObject() + .add("horizontalPositionPercentage", s.getHorizontalPositionPercentage()) + .add("verticalPositionPercentage", s.getVerticalPositionPercentage()) + .add("overrideInlineCueConfig", s.isOverrideInlineCueConfig()) + .add("horizontalAlignment", s.getSubtitleHorizontalPosition().name()); + } + + static GsonObject toJson(Typeface s) { + final GsonObject object = new GsonObject() + .add("bold", s.isBold()) + .add("italic", s.isItalic()); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + object.add("weight", s.getWeight()); + } + + return object; + } + + static GsonObject toJson(PKTrackConfig s) { + return new GsonObject() + .add("preferredMode", s.getPreferredMode().name()) + .add("language", s.getTrackLanguage()); + } + + static GsonObject toJson(PKMaxVideoSize s) { + return new GsonObject() + .add("width", s.getMaxVideoWidth()) + .add("height", s.getMaxVideoHeight()); + } + + static GsonObject toJson(LoadControlBuffers s) { + return new GsonObject() + .addTime("minBufferSec", s.getMinPlayerBufferMs()) + .addTime("maxBufferSec", s.getMaxPlayerBufferMs()) + .addTime("minBufferAfterReBufferSec", s.getMinBufferAfterReBufferMs()) + .addTime("minBufferAfterInteractionSec", s.getMinBufferAfterInteractionMs()) + .addTime("backBufferDurationSec", s.getBackBufferDurationMs()) + .addTime("allowedVideoJoiningTimeSec", s.getAllowedVideoJoiningTimeMs()) + .add("retainBackBufferFromKeyframe", s.getRetainBackBufferFromKeyframe()); + } + + static GsonObject toJson(PlayerSettings settings) { + return new GsonObject() + .add("clearLead", settings.allowClearLead()) + .add("cea608Captions", settings.cea608CaptionsEnabled()) + .add("decoderFallback", settings.enableDecoderFallback()) + .add("abrSettings", toJson(settings.getAbrSettings())) + .add("aspectRatioResizeMode", settings.getAspectRatioResizeMode().name()) + .add("loadControlBuffers", toJson(settings.getLoadControlBuffers())) + .add("maxAudioBitrate", settings.getMaxAudioBitrate()) + .add("maxAudioChannelCount", settings.getMaxAudioChannelCount()) + .add("maxVideoBitrate", settings.getMaxVideoBitrate()) + .add("maxVideoSize", toJson(settings.getMaxVideoSize())) + .add("preferredAudioTrack", toJson(settings.getPreferredAudioTrackConfig())) + .add("preferredMediaFormat", settings.getPreferredMediaFormat().name()) + .add("preferredTextTrack", toJson(settings.getPreferredTextTrackConfig())) + .add("subtitleStyle", toJson(settings.getSubtitleStyleSettings())) + .add("vr", toJson(settings.getVRSettings())) + .add("adAutoPlayOnResume", settings.isAdAutoPlayOnResume()) + .add("forceSinglePlayerEngine", settings.isForceSinglePlayerEngine()) + .add("handleAudioBecomingNoisy", settings.isHandleAudioBecomingNoisyEnabled()) + .add("secureSurface", settings.isSurfaceSecured()) + .add("tunneledAudioPlayback", settings.isTunneledAudioPlayback()) + .add("vrEnabled", settings.isVRPlayerEnabled()); + } + + static GsonObject buildInfoJson() { + return new GsonObject() + .add("apiLevel", Build.VERSION.SDK_INT) + .add("chipset", MediaSupport.DEVICE_CHIPSET) + .add("brand", Build.BRAND) + .add("model", Build.MODEL) + .add("manufacturer", Build.MANUFACTURER) + .add("device", Build.DEVICE) + .add("tags", Build.TAGS) + .add("fingerprint", Build.FINGERPRINT); + } +} From b0004c9e0cebf980f361ec7059f5821181f8b46f Mon Sep 17 00:00:00 2001 From: Noam Tamim Date: Wed, 10 Jun 2020 22:35:11 +0300 Subject: [PATCH 09/14] Null protections when logging player settings --- .../profiler/ExoPlayerProfilingListener.java | 1 - .../com/kaltura/playkit/profiler/ToJson.java | 67 ++++++++++++++++--- 2 files changed, 56 insertions(+), 12 deletions(-) diff --git a/playkit/src/main/java/com/kaltura/playkit/profiler/ExoPlayerProfilingListener.java b/playkit/src/main/java/com/kaltura/playkit/profiler/ExoPlayerProfilingListener.java index b430ca997..b324756f0 100644 --- a/playkit/src/main/java/com/kaltura/playkit/profiler/ExoPlayerProfilingListener.java +++ b/playkit/src/main/java/com/kaltura/playkit/profiler/ExoPlayerProfilingListener.java @@ -338,7 +338,6 @@ private void logLoadingEvent(String event, MediaSourceEventListener.LoadEventInf @Override public void onLoadStarted(EventTime eventTime, MediaSourceEventListener.LoadEventInfo loadEventInfo, MediaSourceEventListener.MediaLoadData mediaLoadData) { -// logLoadingEvent("LoadStarted", loadEventInfo, mediaLoadData, null, null); profiler.maybeLogServerInfo(loadEventInfo.uri); } diff --git a/playkit/src/main/java/com/kaltura/playkit/profiler/ToJson.java b/playkit/src/main/java/com/kaltura/playkit/profiler/ToJson.java index b43845e25..cd25656ad 100644 --- a/playkit/src/main/java/com/kaltura/playkit/profiler/ToJson.java +++ b/playkit/src/main/java/com/kaltura/playkit/profiler/ToJson.java @@ -3,6 +3,10 @@ import android.graphics.Typeface; import android.os.Build; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.google.gson.Gson; import com.google.gson.JsonArray; import com.kaltura.android.exoplayer2.text.CaptionStyleCompat; import com.kaltura.playkit.PKDrmParams; @@ -20,7 +24,8 @@ import com.kaltura.playkit.player.vr.VRSettings; public class ToJson { - static GsonObject toJson(PKMediaEntry entry) { + @Nullable + static GsonObject toJson(@Nullable PKMediaEntry entry) { if (entry == null) { return null; @@ -40,7 +45,11 @@ static GsonObject toJson(PKMediaEntry entry) { .add("sources", sources); } - static GsonObject toJson(PKMediaSource source) { + @NonNull + static GsonObject toJson(@Nullable PKMediaSource source) { + if (source == null) { + return new GsonObject(); + } JsonArray drmParams = new JsonArray(); if (source.hasDrmParams()) { for (PKDrmParams params : source.getDrmData()) { @@ -58,14 +67,22 @@ static GsonObject toJson(PKMediaSource source) { .add("drm", drmParams); } - static GsonObject toJson(ABRSettings s) { + @Nullable + static GsonObject toJson(@Nullable ABRSettings s) { + if (s == null) { + return null; + } return new GsonObject() .add("initialBitrateEstimate", s.getInitialBitrateEstimate()) .add("maxVideoBitrate", s.getMaxVideoBitrate()) .add("minVideoBitrate", s.getMinVideoBitrate()); } - static GsonObject toJson(VRSettings s) { + @Nullable + static GsonObject toJson(@Nullable VRSettings s) { + if (s == null) { + return null; + } return new GsonObject() .add("flingEnabled", s.isFlingEnabled()) .add("vrModeEnabled", s.isVrModeEnabled()) @@ -73,7 +90,11 @@ static GsonObject toJson(VRSettings s) { .add("interactionMode", s.getInteractionMode().name()); } - static GsonObject toJson(SubtitleStyleSettings s) { + @Nullable + static GsonObject toJson(@Nullable SubtitleStyleSettings s) { + if (s == null) { + return null; + } return new GsonObject() .add("textColor", Integer.toHexString(s.getTextColor())) .add("backgroundColor", Integer.toHexString(s.getBackgroundColor())) @@ -103,7 +124,11 @@ private static String edgeTypeName(@CaptionStyleCompat.EdgeType int edgeType) { } } - static GsonObject toJson(PKSubtitlePosition s) { + @Nullable + static GsonObject toJson(@Nullable PKSubtitlePosition s) { + if (s == null) { + return null; + } return new GsonObject() .add("horizontalPositionPercentage", s.getHorizontalPositionPercentage()) .add("verticalPositionPercentage", s.getVerticalPositionPercentage()) @@ -111,7 +136,11 @@ static GsonObject toJson(PKSubtitlePosition s) { .add("horizontalAlignment", s.getSubtitleHorizontalPosition().name()); } - static GsonObject toJson(Typeface s) { + @Nullable + static GsonObject toJson(@Nullable Typeface s) { + if (s == null) { + return null; + } final GsonObject object = new GsonObject() .add("bold", s.isBold()) .add("italic", s.isItalic()); @@ -123,19 +152,31 @@ static GsonObject toJson(Typeface s) { return object; } - static GsonObject toJson(PKTrackConfig s) { + @Nullable + static GsonObject toJson(@Nullable PKTrackConfig s) { + if (s == null) { + return null; + } return new GsonObject() .add("preferredMode", s.getPreferredMode().name()) .add("language", s.getTrackLanguage()); } - static GsonObject toJson(PKMaxVideoSize s) { + @Nullable + static GsonObject toJson(@Nullable PKMaxVideoSize s) { + if (s == null) { + return null; + } return new GsonObject() .add("width", s.getMaxVideoWidth()) .add("height", s.getMaxVideoHeight()); } - static GsonObject toJson(LoadControlBuffers s) { + @Nullable + static GsonObject toJson(@Nullable LoadControlBuffers s) { + if (s == null) { + return null; + } return new GsonObject() .addTime("minBufferSec", s.getMinPlayerBufferMs()) .addTime("maxBufferSec", s.getMaxPlayerBufferMs()) @@ -146,7 +187,11 @@ static GsonObject toJson(LoadControlBuffers s) { .add("retainBackBufferFromKeyframe", s.getRetainBackBufferFromKeyframe()); } - static GsonObject toJson(PlayerSettings settings) { + @Nullable + static GsonObject toJson(@Nullable PlayerSettings settings) { + if (settings == null) { + return null; + } return new GsonObject() .add("clearLead", settings.allowClearLead()) .add("cea608Captions", settings.cea608CaptionsEnabled()) From 86d2a1b0f6a68467d5ec6161233afaa47bb870b5 Mon Sep 17 00:00:00 2001 From: Gilad Nadav Date: Tue, 20 Apr 2021 01:00:26 +0300 Subject: [PATCH 10/14] seek processed --- .../kaltura/playkit/profiler/ExoPlayerProfilingListener.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/playkit/src/main/java/com/kaltura/playkit/profiler/ExoPlayerProfilingListener.java b/playkit/src/main/java/com/kaltura/playkit/profiler/ExoPlayerProfilingListener.java index 90ee4991c..11e619b31 100644 --- a/playkit/src/main/java/com/kaltura/playkit/profiler/ExoPlayerProfilingListener.java +++ b/playkit/src/main/java/com/kaltura/playkit/profiler/ExoPlayerProfilingListener.java @@ -160,7 +160,8 @@ public void onPositionDiscontinuity(EventTime eventTime, int reason) { reasonString = "PeriodTransition"; break; case DISCONTINUITY_REASON_SEEK: - reasonString = "SeekProcessed"; + log("SeekProcessed").end(); + reasonString = "Seek"; break; case DISCONTINUITY_REASON_SEEK_ADJUSTMENT: reasonString = "SeekAdjustment"; From 8102605bb1bd87551b37eb2aeb3eefe9c05ba400 Mon Sep 17 00:00:00 2001 From: Gourav Saxena Date: Wed, 16 Mar 2022 20:32:58 +0530 Subject: [PATCH 11/14] - Fixed the APIs --- .../com/kaltura/playkit/profiler/ToJson.java | 58 +++++++++---------- 1 file changed, 28 insertions(+), 30 deletions(-) diff --git a/playkit/src/main/java/com/kaltura/playkit/profiler/ToJson.java b/playkit/src/main/java/com/kaltura/playkit/profiler/ToJson.java index cd25656ad..7df719e31 100644 --- a/playkit/src/main/java/com/kaltura/playkit/profiler/ToJson.java +++ b/playkit/src/main/java/com/kaltura/playkit/profiler/ToJson.java @@ -6,9 +6,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.google.gson.Gson; import com.google.gson.JsonArray; -import com.kaltura.android.exoplayer2.text.CaptionStyleCompat; +import com.kaltura.android.exoplayer2.ui.CaptionStyleCompat; import com.kaltura.playkit.PKDrmParams; import com.kaltura.playkit.PKMediaEntry; import com.kaltura.playkit.PKMediaSource; @@ -17,7 +16,6 @@ import com.kaltura.playkit.player.ABRSettings; import com.kaltura.playkit.player.LoadControlBuffers; import com.kaltura.playkit.player.MediaSupport; -import com.kaltura.playkit.player.PKMaxVideoSize; import com.kaltura.playkit.player.PKSubtitlePosition; import com.kaltura.playkit.player.PlayerSettings; import com.kaltura.playkit.player.SubtitleStyleSettings; @@ -158,18 +156,18 @@ static GsonObject toJson(@Nullable PKTrackConfig s) { return null; } return new GsonObject() - .add("preferredMode", s.getPreferredMode().name()) - .add("language", s.getTrackLanguage()); + .add("preferredMode", s.getPreferredMode().name()) + .add("language", s.getTrackLanguage()); } @Nullable - static GsonObject toJson(@Nullable PKMaxVideoSize s) { - if (s == null) { + static GsonObject toJson(@Nullable Long height, Long width) { + if (height == null || width == null) { return null; } return new GsonObject() - .add("width", s.getMaxVideoWidth()) - .add("height", s.getMaxVideoHeight()); + .add("width", height) + .add("height", width); } @Nullable @@ -193,27 +191,27 @@ static GsonObject toJson(@Nullable PlayerSettings settings) { return null; } return new GsonObject() - .add("clearLead", settings.allowClearLead()) - .add("cea608Captions", settings.cea608CaptionsEnabled()) - .add("decoderFallback", settings.enableDecoderFallback()) - .add("abrSettings", toJson(settings.getAbrSettings())) - .add("aspectRatioResizeMode", settings.getAspectRatioResizeMode().name()) - .add("loadControlBuffers", toJson(settings.getLoadControlBuffers())) - .add("maxAudioBitrate", settings.getMaxAudioBitrate()) - .add("maxAudioChannelCount", settings.getMaxAudioChannelCount()) - .add("maxVideoBitrate", settings.getMaxVideoBitrate()) - .add("maxVideoSize", toJson(settings.getMaxVideoSize())) - .add("preferredAudioTrack", toJson(settings.getPreferredAudioTrackConfig())) - .add("preferredMediaFormat", settings.getPreferredMediaFormat().name()) - .add("preferredTextTrack", toJson(settings.getPreferredTextTrackConfig())) - .add("subtitleStyle", toJson(settings.getSubtitleStyleSettings())) - .add("vr", toJson(settings.getVRSettings())) - .add("adAutoPlayOnResume", settings.isAdAutoPlayOnResume()) - .add("forceSinglePlayerEngine", settings.isForceSinglePlayerEngine()) - .add("handleAudioBecomingNoisy", settings.isHandleAudioBecomingNoisyEnabled()) - .add("secureSurface", settings.isSurfaceSecured()) - .add("tunneledAudioPlayback", settings.isTunneledAudioPlayback()) - .add("vrEnabled", settings.isVRPlayerEnabled()); + .add("clearLead", settings.allowClearLead()) + .add("cea608Captions", settings.cea608CaptionsEnabled()) + .add("decoderFallback", settings.enableDecoderFallback()) + .add("abrSettings", toJson(settings.getAbrSettings())) + .add("aspectRatioResizeMode", settings.getAspectRatioResizeMode().name()) + .add("loadControlBuffers", toJson(settings.getLoadControlBuffers())) + .add("maxAudioBitrate", settings.getMaxAudioBitrate()) + .add("maxAudioChannelCount", settings.getMaxAudioChannelCount()) + .add("maxVideoBitrate", settings.getAbrSettings().getMaxVideoBitrate()) + .add("maxVideoSize", toJson(settings.getAbrSettings().getMaxVideoHeight(), settings.getAbrSettings().getMaxVideoWidth())) + .add("preferredAudioTrack", toJson(settings.getPreferredAudioTrackConfig())) + .add("preferredMediaFormat", settings.getPreferredMediaFormat().name()) + .add("preferredTextTrack", toJson(settings.getPreferredTextTrackConfig())) + .add("subtitleStyle", toJson(settings.getSubtitleStyleSettings())) + .add("vr", toJson(settings.getVRSettings())) + .add("adAutoPlayOnResume", settings.isAdAutoPlayOnResume()) + .add("forceSinglePlayerEngine", settings.isForceSinglePlayerEngine()) + .add("handleAudioBecomingNoisy", settings.isHandleAudioBecomingNoisyEnabled()) + .add("secureSurface", settings.isSurfaceSecured()) + .add("tunneledAudioPlayback", settings.isTunneledAudioPlayback()) + .add("vrEnabled", settings.isVRPlayerEnabled()); } static GsonObject buildInfoJson() { From f2bcb5e3dfcf2838daabe59e5b6c3f0fabff609e Mon Sep 17 00:00:00 2001 From: Gourav Saxena Date: Mon, 21 Mar 2022 12:09:57 +0530 Subject: [PATCH 12/14] - Added `.end()` for missing log - cleanup --- .../profiler/ExoPlayerProfilingListener.java | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/playkit/src/main/java/com/kaltura/playkit/profiler/ExoPlayerProfilingListener.java b/playkit/src/main/java/com/kaltura/playkit/profiler/ExoPlayerProfilingListener.java index a18216c59..9f1691572 100644 --- a/playkit/src/main/java/com/kaltura/playkit/profiler/ExoPlayerProfilingListener.java +++ b/playkit/src/main/java/com/kaltura/playkit/profiler/ExoPlayerProfilingListener.java @@ -1,6 +1,5 @@ package com.kaltura.playkit.profiler; - import android.util.Pair; import androidx.annotation.NonNull; @@ -237,12 +236,18 @@ public void onIsLoadingChanged(@NonNull EventTime eventTime, boolean isLoading) @Override public void onAudioCodecError(@NonNull EventTime eventTime, Exception audioCodecError) { - log("PlayerError").add("type", "audioCodecError").add("cause", audioCodecError.getCause()); + log("PlayerError") + .add("type", "audioCodecError") + .add("cause", audioCodecError.getCause()) + .end(); } @Override public void onAudioSinkError(@NonNull EventTime eventTime, Exception audioSinkError) { - log("PlayerError").add("type", "audioSinkError").add("cause", audioSinkError.getCause()); + log("PlayerError") + .add("type", "audioSinkError") + .add("cause", audioSinkError.getCause()) + .end(); } @Override @@ -476,13 +481,15 @@ public void onRenderedFirstFrame(@NonNull EventTime eventTime, @NonNull Object o public void onSurfaceSizeChanged(@NonNull EventTime eventTime, int width, int height) { log("ViewportSizeChange") .add("width", width) - .add("height", height).end(); + .add("height", height) + .end(); } @Override public void onVolumeChanged(@NonNull EventTime eventTime, float volume) { log("VolumeChanged") - .add("volume", volume).end(); + .add("volume", volume) + .end(); } @Override From 5ac81a25f3c04498579a98e2b14f923343fca85d Mon Sep 17 00:00:00 2001 From: Gourav Saxena Date: Mon, 21 Mar 2022 13:45:31 +0530 Subject: [PATCH 13/14] - Removed deprecated API `onTracksChanged` instead we have `onTracksInfoChanged` - Now video and audio format, which is getting selected by the played; will be supplied from `onVideoInputFormatChanged` and `onAudioInputFormatChanged` methods. This will be called whenever video or audio format is changed - In `onTracksInfoChanged`, we don't have info which video or audio is selected; hence this info is not being sent there. In case if Text track is default then this information will go out. --- .../profiler/ExoPlayerProfilingListener.java | 71 +++++++++---------- 1 file changed, 33 insertions(+), 38 deletions(-) diff --git a/playkit/src/main/java/com/kaltura/playkit/profiler/ExoPlayerProfilingListener.java b/playkit/src/main/java/com/kaltura/playkit/profiler/ExoPlayerProfilingListener.java index 9f1691572..826bab119 100644 --- a/playkit/src/main/java/com/kaltura/playkit/profiler/ExoPlayerProfilingListener.java +++ b/playkit/src/main/java/com/kaltura/playkit/profiler/ExoPlayerProfilingListener.java @@ -7,10 +7,12 @@ import com.google.gson.JsonArray; import com.google.gson.JsonObject; +import com.kaltura.android.exoplayer2.C; import com.kaltura.android.exoplayer2.Format; import com.kaltura.android.exoplayer2.PlaybackException; import com.kaltura.android.exoplayer2.PlaybackParameters; import com.kaltura.android.exoplayer2.Player; +import com.kaltura.android.exoplayer2.TracksInfo; import com.kaltura.android.exoplayer2.analytics.AnalyticsListener; import com.kaltura.android.exoplayer2.decoder.DecoderCounters; import com.kaltura.android.exoplayer2.decoder.DecoderReuseEvaluation; @@ -18,11 +20,6 @@ import com.kaltura.android.exoplayer2.metadata.Metadata; import com.kaltura.android.exoplayer2.source.LoadEventInfo; import com.kaltura.android.exoplayer2.source.MediaLoadData; -import com.kaltura.android.exoplayer2.source.TrackGroup; -import com.kaltura.android.exoplayer2.source.TrackGroupArray; -import com.kaltura.android.exoplayer2.trackselection.ExoTrackSelection; -import com.kaltura.android.exoplayer2.trackselection.TrackSelection; -import com.kaltura.android.exoplayer2.trackselection.TrackSelectionArray; import com.kaltura.android.exoplayer2.video.VideoSize; import com.kaltura.playkit.PKPlaybackException; import com.kaltura.playkit.Utils; @@ -30,7 +27,6 @@ import com.kaltura.playkit.player.Profiler.Event; import java.io.IOException; -import java.util.LinkedHashSet; import static com.kaltura.android.exoplayer2.C.DATA_TYPE_AD; import static com.kaltura.android.exoplayer2.C.DATA_TYPE_DRM; @@ -269,45 +265,29 @@ public void onPlayerError(@NonNull EventTime eventTime, @NonNull PlaybackExcepti } @Override - public void onTracksChanged(@NonNull EventTime eventTime, TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { - LinkedHashSet trackGroupSet = new LinkedHashSet<>(trackGroups.length); - for (int i = 0; i < trackSelections.length; i++) { - final TrackSelection trackSelection = trackSelections.get(i); - if (trackSelection != null) { - trackGroupSet.add(trackSelection.getTrackGroup()); - } - } - - // Add the rest - for (int i = 0; i < trackGroups.length; i++) { - final TrackGroup trackGroup = trackGroups.get(i); - trackGroupSet.add(trackGroup); + public void onTracksInfoChanged(@NonNull EventTime eventTime, @NonNull TracksInfo tracksInfo) { + if (tracksInfo.getTrackGroupInfos().size() <= 0) { + return; } - JsonArray jTrackGroups = new JsonArray(trackGroups.length); - for (TrackGroup trackGroup : trackGroupSet) { - JsonArray jTrackGroup = new JsonArray(trackGroup.length); - for (int j = 0; j < trackGroup.length; j++) { - final Format format = trackGroup.getFormat(j); - jTrackGroup.add(toJSON(format)); + // All the formats will be added to the TrackGroup's JSON + JsonArray jTrackGroups = new JsonArray(tracksInfo.getTrackGroupInfos().size()); + JsonArray jTextTrackSelections = new JsonArray(); + + for (TracksInfo.TrackGroupInfo trackGroupInfo : tracksInfo.getTrackGroupInfos()) { + JsonArray jTrackGroup = new JsonArray(trackGroupInfo.getTrackGroup().length); + for (int trackGroupIndex = 0; trackGroupIndex < trackGroupInfo.getTrackGroup().length; trackGroupIndex++) { + jTrackGroup.add(toJSON(trackGroupInfo.getTrackGroup().getFormat(trackGroupIndex))); + if (trackGroupInfo.getTrackType() == C.TRACK_TYPE_TEXT && trackGroupInfo.isSelected()) { + jTextTrackSelections.add(toJSON(trackGroupInfo.getTrackGroup().getFormat(trackGroupIndex))); + } } jTrackGroups.add(jTrackGroup); } - - JsonArray jTrackSelections = new JsonArray(trackSelections.length); - for (int i = 0; i < trackSelections.length; i++) { - ExoTrackSelection trackSelection = null; - if (trackSelections.get(i) instanceof ExoTrackSelection) { - trackSelection = (ExoTrackSelection) trackSelections.get(i); - } - final Format selectedFormat = trackSelection == null ? null : trackSelection.getSelectedFormat(); - jTrackSelections.add(toJSON(selectedFormat)); - } - log("TracksChanged") .add("available", jTrackGroups) - .add("selected", jTrackSelections) + .add("selected", jTextTrackSelections) .end(); } @@ -436,12 +416,27 @@ public void onVideoDecoderInitialized(@NonNull EventTime eventTime, @NonNull Str } @Override - public void onVideoInputFormatChanged(@NonNull EventTime eventTime, Format format, DecoderReuseEvaluation decoderReuseEvaluation) { + public void onVideoInputFormatChanged(@NonNull EventTime eventTime, @NonNull Format format, DecoderReuseEvaluation decoderReuseEvaluation) { log("DecoderInputFormatChanged") .add("id", format.id) .add("codecs", format.codecs) .add("bitrate", format.bitrate) .end(); + + JsonArray jVideoTrackSelections = new JsonArray(1); + jVideoTrackSelections.add(toJSON(format)); + log("TracksChanged") + .add("selected", jVideoTrackSelections) + .end(); + } + + @Override + public void onAudioInputFormatChanged(@NonNull EventTime eventTime, @NonNull Format format, @Nullable DecoderReuseEvaluation decoderReuseEvaluation) { + JsonArray jAudioTrackSelections = new JsonArray(1); + jAudioTrackSelections.add(toJSON(format)); + log("TracksChanged") + .add("selected", jAudioTrackSelections) + .end(); } @Override From c33ed8bde1bf0b3d719f7b887844acb2d51e2c51 Mon Sep 17 00:00:00 2001 From: Gourav Saxena Date: Wed, 27 Apr 2022 14:41:14 +0530 Subject: [PATCH 14/14] - Configurable device capability report. Default is enabled. - Removed profiler config code from Playkit Manager --- .../com/kaltura/playkit/PlayKitManager.java | 18 +++++++----------- .../main/java/com/kaltura/playkit/Utils.java | 3 ++- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/playkit/src/main/java/com/kaltura/playkit/PlayKitManager.java b/playkit/src/main/java/com/kaltura/playkit/PlayKitManager.java index 702dfdd6a..e2b920128 100644 --- a/playkit/src/main/java/com/kaltura/playkit/PlayKitManager.java +++ b/playkit/src/main/java/com/kaltura/playkit/PlayKitManager.java @@ -56,29 +56,25 @@ static PKPlugin createPlugin(String name) { } public static Player loadPlayer(Context context, @Nullable PKPluginConfigs pluginConfigs, MessageBus messageBus) { - MediaSupport.initializeDrm(context, null); - initializeProfiler(context); - PKDeviceCapabilities.maybeSendReport(context); + if (shouldSendDeviceCapabilitiesReport) { + PKDeviceCapabilities.maybeSendReport(context); + } PlayerLoader playerLoader = new PlayerLoader(context, messageBus); playerLoader.load(pluginConfigs != null ? pluginConfigs : new PKPluginConfigs()); return playerLoader; } - private static void initializeProfiler(Context context) { - Gson gson = new Gson(); - // TODO: 07/06/2020 This should come from the backend - final String json = "{\"postURL\": \"https://dtvqq1tbxf.execute-api.us-east-1.amazonaws.com/default/profilerLogCollector\", \"sendPercentage\": 100}"; - final ProfilerConfig profilerConfig = gson.fromJson(json, ProfilerConfig.class); - PlayKitProfiler.init(context, profilerConfig); - } - public static Player loadPlayer(Context context, @Nullable PKPluginConfigs pluginConfigs) { return loadPlayer(context, pluginConfigs, null); } + public static void disableDeviceCapabilitiesReport() { + shouldSendDeviceCapabilitiesReport = false; + } + public static final class ProfilerConfig { public String postURL; public float sendPercentage; diff --git a/playkit/src/main/java/com/kaltura/playkit/Utils.java b/playkit/src/main/java/com/kaltura/playkit/Utils.java index 6bc50a41d..b33815254 100644 --- a/playkit/src/main/java/com/kaltura/playkit/Utils.java +++ b/playkit/src/main/java/com/kaltura/playkit/Utils.java @@ -48,6 +48,7 @@ import static android.content.Context.UI_MODE_SERVICE; import static com.kaltura.playkit.utils.Consts.HTTP_METHOD_GET; import static com.kaltura.playkit.utils.Consts.HTTP_METHOD_POST; +import static com.kaltura.playkit.utils.Consts.MILLISECONDS_MULTIPLIER_FLOAT; /** * @hide @@ -363,7 +364,7 @@ public GsonObject addAll(@Nullable GsonObject otherGo) { } public GsonObject addTime(String key, long millis) { - jo.addProperty(key, millis / 1000f); + jo.addProperty(key, millis / MILLISECONDS_MULTIPLIER_FLOAT); return this; }