Skip to content

Commit

Permalink
Update QoS models
Browse files Browse the repository at this point in the history
  • Loading branch information
MGaetan89 committed Jul 30, 2024
1 parent 2ebab7a commit aa4a8f9
Show file tree
Hide file tree
Showing 14 changed files with 357 additions and 174 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@ import ch.srgssr.pillarbox.player.analytics.metrics.MetricsCollector
import ch.srgssr.pillarbox.player.analytics.metrics.PlaybackMetrics
import ch.srgssr.pillarbox.player.qos.models.QoSError
import ch.srgssr.pillarbox.player.qos.models.QoSEvent
import ch.srgssr.pillarbox.player.qos.models.QoSMedia
import ch.srgssr.pillarbox.player.qos.models.QoSMessage
import ch.srgssr.pillarbox.player.qos.models.QoSSession
import ch.srgssr.pillarbox.player.qos.models.QoSSessionTimings
import ch.srgssr.pillarbox.player.qos.models.QoSStall
import ch.srgssr.pillarbox.player.runOnApplicationLooper
import ch.srgssr.pillarbox.player.utils.BitrateUtil.toByteRate
import ch.srgssr.pillarbox.player.utils.DebugLogger
Expand Down Expand Up @@ -68,11 +70,11 @@ internal class QoSCoordinator(
sessionManager.getSessionById(metrics.sessionId)?.let {
sendStartEvent(
session = it,
timings = QoSSessionTimings(
asset = metrics.loadDuration.asset,
mediaSource = metrics.loadDuration.source,
currentToStart = metrics.loadDuration.timeToReady,
drm = metrics.loadDuration.drm
timeMetrics = QoSSessionTimings(
asset = metrics.loadDuration.source,
drm = metrics.loadDuration.drm,
metadata = metrics.loadDuration.asset,
total = metrics.loadDuration.timeToReady,
)
)
}
Expand Down Expand Up @@ -131,8 +133,10 @@ internal class QoSCoordinator(
bufferDuration = player.totalBufferedDuration,
playbackDuration = playbackDuration.inWholeMilliseconds,
playerPosition = player.currentPosition,
stallCount = stallCount,
stallDuration = stallDuration.inWholeSeconds,
stall = QoSStall(
count = stallCount,
duration = stallDuration.inWholeMilliseconds,
),
url = url.toString(),
)
}
Expand Down Expand Up @@ -171,6 +175,7 @@ internal class QoSCoordinator(
throwable = it,
playerPosition = player.currentPosition,
severity = QoSError.Severity.FATAL,
url = url,
),
)
}
Expand All @@ -185,16 +190,20 @@ internal class QoSCoordinator(

private fun sendStartEvent(
session: PlaybackSessionManager.Session,
timings: QoSSessionTimings,
timeMetrics: QoSSessionTimings,
) {
sendEvent(
eventName = "START",
session = session,
data = QoSSession(
context = context,
mediaId = session.mediaItem.mediaId,
mediaSource = session.mediaItem.localConfiguration?.uri.toString(),
timings = timings,
media = QoSMedia(
assetUrl = url,
id = session.mediaItem.mediaId,
metadataUrl = session.mediaItem.localConfiguration?.uri.toString(),
origin = context.packageName,
),
timeMetrics = timeMetrics,
),
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright (c) SRG SSR. All rights reserved.
* License information is available from the LICENSE file.
*/
package ch.srgssr.pillarbox.player.qos.models

/**
* Information about the device.
*
* @property id The unique identifier of the device.
* @property model The model of the device.
* @property type The type of device.
*/
data class QoSDevice(
val id: String,
val model: String,
val type: DeviceType,
) {
/**
* The type of device.
*/
enum class DeviceType {
CAR,
DESKTOP,
PHONE,
TABLET,
TV,
UNKNOWN,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ package ch.srgssr.pillarbox.player.qos.models
* @property name The name of the error.
* @property playerPosition The position of the player when the error occurred, in milliseconds, or `null` if not available.
* @property severity The severity of the error, either [FATAL][Severity.FATAL] or [WARNING][Severity.WARNING].
* @property url The last loaded url.
*/
data class QoSError(
val log: String,
val message: String,
val name: String,
val playerPosition: Long?,
val severity: Severity,
val url: String,
) {
/**
* Represents a [Player][androidx.media3.common.Player] error severity.
Expand All @@ -32,11 +34,13 @@ data class QoSError(
throwable: Throwable,
playerPosition: Long?,
severity: Severity,
url: String,
) : this(
log = throwable.stackTraceToString(),
message = throwable.message.orEmpty(),
name = throwable::class.simpleName.orEmpty(),
playerPosition = playerPosition,
severity = severity,
url = url,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ package ch.srgssr.pillarbox.player.qos.models
* @property bufferDuration The forward duration of the buffer, in milliseconds.
* @property playbackDuration The duration of the playback, in milliseconds.
* @property playerPosition The position of the player, in milliseconds.
* @property stallCount The number of stalls that have occurred, not as a result of a seek.
* @property stallDuration The total duration of the stalls, in milliseconds.
* @property stall The information about stalls.
* @property url The URL of the stream.
*/
data class QoSEvent(
Expand All @@ -22,7 +21,6 @@ data class QoSEvent(
val bufferDuration: Long,
val playbackDuration: Long,
val playerPosition: Long,
val stallCount: Int,
val stallDuration: Long,
val stall: QoSStall,
val url: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright (c) SRG SSR. All rights reserved.
* License information is available from the LICENSE file.
*/
package ch.srgssr.pillarbox.player.qos.models

/**
* Information about the media being played.
*
* @property assetUrl The URL of the asset.
* @property id The id of the media.
* @property metadataUrl The URL of the metadata.
* @property origin The origin of the media.
*/
data class QoSMedia(
val assetUrl: String,
val id: String,
val metadataUrl: String,
val origin: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* Copyright (c) SRG SSR. All rights reserved.
* License information is available from the LICENSE file.
*/
package ch.srgssr.pillarbox.player.qos.models

/**
* Information about the operating system.
*
* @property name The name of the operating system.
* @property version The version of the operating system.
*/
data class QoSOS(
val name: String,
val version: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* Copyright (c) SRG SSR. All rights reserved.
* License information is available from the LICENSE file.
*/
package ch.srgssr.pillarbox.player.qos.models

/**
* Information about the player.
*
* @property name The name of the player.
* @property platform The platform of the player.
* @property version The version of the player.
*/
data class QoSPlayer(
val name: String,
val platform: String,
val version: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* Copyright (c) SRG SSR. All rights reserved.
* License information is available from the LICENSE file.
*/
package ch.srgssr.pillarbox.player.qos.models

/**
* Information about the device screen.
*
* @property height The height of the screen, in pixels.
* @property width The width of the screen, in pixels.
*/
data class QoSScreen(
val height: Int,
val width: Int,
)
Original file line number Diff line number Diff line change
Expand Up @@ -10,65 +10,51 @@ import android.graphics.Rect
import android.os.Build
import android.view.WindowManager
import ch.srgssr.pillarbox.player.BuildConfig
import ch.srgssr.pillarbox.player.qos.models.QoSDevice.DeviceType

/**
* Represents a QoS session, which contains information about the device, current media, and player.
*
* @property deviceId The unique identifier of the device.
* @property deviceModel The model of the device.
* @property deviceType The type of device.
* @property mediaId The identifier of the media being played.
* @property mediaSource The source URL of the media being played.
* @property operatingSystemName The name of the operating system.
* @property operatingSystemVersion The version of the operating system.
* @property origin The origin of the player.
* @property playerName The name of the player.
* @property playerPlatform The platform of the player.
* @property playerVersion The version of the player.
* @property screenHeight The height of the screen in pixels.
* @property screenWidth The width of the screen in pixels.
* @property timings The timing until the current media started to play.
* @property device The information about the device.
* @property media The information about the media being played.
* @property operatingSystem The information about the operating system.
* @property player The information about the player.
* @property screen The information about the device screen.
* @property timeMetrics The metrics about the time needed to load the various media components.
*/
data class QoSSession(
val deviceId: String,
val deviceModel: String = getDeviceModel(),
val deviceType: DeviceType,
val mediaId: String,
val mediaSource: String,
val operatingSystemName: String = PLATFORM_NAME,
val operatingSystemVersion: String = OPERATING_SYSTEM_VERSION,
val origin: String,
val playerName: String = PLAYER_NAME,
val playerPlatform: String = PLATFORM_NAME,
val playerVersion: String = PLAYER_VERSION,
val screenHeight: Int,
val screenWidth: Int,
val timings: QoSSessionTimings = QoSSessionTimings.Empty,
val device: QoSDevice,
val media: QoSMedia,
val operatingSystem: QoSOS = QoSOS(
name = PLATFORM_NAME,
version = OPERATING_SYSTEM_VERSION,
),
val player: QoSPlayer = QoSPlayer(
name = PLAYER_NAME,
platform = PLATFORM_NAME,
version = PLAYER_VERSION,
),
val screen: QoSScreen,
val timeMetrics: QoSSessionTimings = QoSSessionTimings.Empty,
) {
/**
* The type of device.
*/
enum class DeviceType {
CAR,
PHONE,
TABLET,
TV,
}

constructor(
context: Context,
mediaId: String,
mediaSource: String,
timings: QoSSessionTimings
media: QoSMedia,
timeMetrics: QoSSessionTimings,
) : this(
deviceId = getDeviceId(),
deviceType = context.getDeviceType(),
mediaId = mediaId,
mediaSource = mediaSource,
origin = context.packageName,
screenHeight = context.getWindowBounds().height(),
screenWidth = context.getWindowBounds().width(),
timings = timings
device = QoSDevice(
id = getDeviceId(),
model = getDeviceModel(),
type = context.getDeviceType(),
),
media = media,
screen = context.getWindowBounds().let { windowBounds ->
QoSScreen(
height = windowBounds.height(),
width = windowBounds.width(),
)
},
timeMetrics = timeMetrics,
)

private companion object {
Expand All @@ -92,6 +78,7 @@ data class QoSSession(
val configuration = resources.configuration
return when (configuration.uiMode and Configuration.UI_MODE_TYPE_MASK) {
Configuration.UI_MODE_TYPE_CAR -> DeviceType.CAR
Configuration.UI_MODE_TYPE_DESK -> DeviceType.DESKTOP
Configuration.UI_MODE_TYPE_NORMAL -> {
val smallestWidthDp = configuration.smallestScreenWidthDp

Expand All @@ -103,7 +90,7 @@ data class QoSSession(
}

Configuration.UI_MODE_TYPE_TELEVISION -> DeviceType.TV
else -> DeviceType.PHONE // TODO Do we assume PHONE by default? Or do we throw an exception?
else -> DeviceType.UNKNOWN
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,29 @@ import kotlin.time.Duration
* Represents the timings until the current media started to play.
*
* @property asset The time spent to load the asset.
* @property currentToStart The time spent to load from the moment the [MediaItem][androidx.media3.common.MediaItem] became the current item until it
* started to play.
* @property drm The time spent to load the DRM.
* @property mediaSource The time spent to load the media source.
* @property metadata The time spent to load the media source.
* @property token The time spent to load the token.
* @property total The time spent to load from the moment the [MediaItem][androidx.media3.common.MediaItem] became the current item until it
* started to play.
*/
data class QoSSessionTimings(
val asset: Duration? = null,
val currentToStart: Duration? = null,
val drm: Duration? = null,
val mediaSource: Duration? = null,
val metadata: Duration? = null,
val token: Duration? = null,
val total: Duration? = null,
) {
companion object {
/**
* Default [QoSSessionTimings] where all fields are set to `null`.
*/
val Empty = QoSSessionTimings(
asset = null,
currentToStart = null,
drm = null,
mediaSource = null,
metadata = null,
token = null,
total = null,
)
}
}
Loading

0 comments on commit aa4a8f9

Please sign in to comment.