From 8256bc71e2462111c403471b67015dba044e3042 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Muller?= Date: Tue, 19 Nov 2024 14:32:57 +0100 Subject: [PATCH 01/17] Review KDoc in `ch.srgssr.pillarbox.player.analytics.metrics` --- .../analytics/metrics/MetricsCollector.kt | 44 +++++++++++-------- .../analytics/metrics/PlaybackMetrics.kt | 44 +++++++++---------- 2 files changed, 47 insertions(+), 41 deletions(-) diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/analytics/metrics/MetricsCollector.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/analytics/metrics/MetricsCollector.kt index a413cb17a..05afe1a1e 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/analytics/metrics/MetricsCollector.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/analytics/metrics/MetricsCollector.kt @@ -22,26 +22,30 @@ import ch.srgssr.pillarbox.player.utils.DebugLogger import java.io.IOException /** - * Playback stats metrics - * Compute playback stats metrics likes stalls, playtime, bitrate, etc... + * This class is responsible for collecting and computing playback statistics metrics such as: + * - Stalls (count and duration) + * - Playtime + * - Bitrate (estimated and indicated) + * - Loading times (manifest, asset, source, DRM) + * - Video and audio format information + * - Dropped video frames + * - Surface size */ class MetricsCollector private constructor( private val timeProvider: () -> Long, ) { /** - * Listener + * A listener interface for receiving updates about playback metrics. */ interface Listener { /** - * On metric session ready + * Invoked when the player has collected enough information to start reporting playback metrics. * - * @param metrics + * @param metrics The [PlaybackMetrics] object containing various playback metrics. */ fun onMetricSessionReady(metrics: PlaybackMetrics) = Unit } - private val metricsAnalyticsListeners = MetricsAnalyticsListener() - private val metricsSessionManagerListener = MetricsSessionManagerListener() private var currentSession: PlaybackSessionManager.Session? = null private val listeners = mutableSetOf() private lateinit var player: PillarboxExoPlayer @@ -51,27 +55,29 @@ class MetricsCollector private constructor( constructor() : this({ System.currentTimeMillis() }) /** - * Set player at [PillarboxExoPlayer] creation. + * Sets the [PillarboxExoPlayer] instance to be used for analytics and session management. + * + * @param player The [PillarboxExoPlayer] instance to be used. */ fun setPlayer(player: PillarboxExoPlayer) { - player.sessionManager.addListener(metricsSessionManagerListener) - player.addAnalyticsListener(metricsAnalyticsListeners) + player.sessionManager.addListener(MetricsSessionManagerListener()) + player.addAnalyticsListener(MetricsAnalyticsListener()) this.player = player } /** - * Add listener + * Registers a listener to receive events. * - * @param listener + * @param listener The listener to be added. */ fun addListener(listener: Listener) { listeners.add(listener) } /** - * Remove listener + * Removes the specified listener from the list of listeners. * - * @param listener + * @param listener The listener to be removed. */ fun removeListener(listener: Listener) { listeners.remove(listener) @@ -240,9 +246,9 @@ class MetricsCollector private constructor( } /** - * Get current metrics + * Retrieves the current playback metrics. * - * @return metrics to the current time + * @return The playback metrics for the current session, or `null` if there is no active session. */ fun getCurrentMetrics(): PlaybackMetrics? { return currentSession?.let { @@ -277,10 +283,10 @@ class MetricsCollector private constructor( } /** - * Get metrics for session + * Retrieves playback metrics for a given playback session. * - * @param session - * @return + * @param session The playback session for which to retrieve metrics. + * @return A [PlaybackMetrics] containing the session's metrics, or `null` if no metrics are found for the session. */ fun getMetricsForSession(session: PlaybackSessionManager.Session): PlaybackMetrics? { return metricsSessions[session.periodUid]?.let { diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/analytics/metrics/PlaybackMetrics.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/analytics/metrics/PlaybackMetrics.kt index 784d1775f..7c5e0fb43 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/analytics/metrics/PlaybackMetrics.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/analytics/metrics/PlaybackMetrics.kt @@ -12,23 +12,23 @@ import ch.srgssr.pillarbox.player.extension.videoSize import kotlin.time.Duration /** - * Represents a generic event, which contains metrics about the current media stream. + * Represents playback metrics, containing information about the current media stream and playback session. * - * @property sessionId The session ID. + * @property sessionId A unique identifier for the playback session. * @property bandwidth The device-measured network bandwidth, in bits per second. * @property indicatedBitrate The bitrate of the video and audio format, in bits per second. - * @property playbackDuration The duration the session spent playing. - * @property bufferingDuration The duration the session spent in buffering. - * @property stallCount The number of stalls that have occurred, not as a result of a seek. + * @property playbackDuration The total duration spent playing the media. + * @property bufferingDuration The total duration spent buffering the media. + * @property stallCount The number of times playback stalled, excluding stalls caused by seeking. * @property stallDuration The total duration of the stalls. - * @property loadDuration The load duration that could be computed. - * @property totalLoadTime The load time to compute [bandwidth]. - * @property totalBytesLoaded The total bytes loaded to compute [bandwidth]. - * @property url The last url loaded by the player. - * @property videoFormat The current video format selected by the player. - * @property audioFormat The current audio format selected by the player. - * @property surfaceSize The size of the surface connected to the player. [Size.ZERO] if not connected. - * @property totalDroppedFrames The total frame dropped. + * @property loadDuration The [LoadDuration] containing detailed timings for different stages of media loading. + * @property totalLoadTime The total time taken to load the media for bandwidth calculation. + * @property totalBytesLoaded The total number of bytes loaded for bandwidth calculation. + * @property url The last URL loaded by the player. + * @property videoFormat The [Format] of the currently selected video track. + * @property audioFormat The [Format] of the currently selected audio track. + * @property surfaceSize The size of the surface used for rendering the video. If no surface is connected, this will be [Size.ZERO]. + * @property totalDroppedFrames The total number of video frames dropped. */ data class PlaybackMetrics( val sessionId: String, @@ -49,19 +49,19 @@ data class PlaybackMetrics( ) { /** - * Video size of [videoFormat] if applicable. + * Represents the video size of [videoFormat], if applicable. Otherwise [VideoSize.UNKNOWN]. */ val videoSize: VideoSize = videoFormat?.videoSize ?: VideoSize.UNKNOWN /** - * Load duration - * Represents the timings until the current media started to play. - * @property source The time spent to load the media source. - * @property manifest The time spent to load the main manifest if applicable. - * @property asset The time spent to load the asset. - * @property drm The time spent to load the DRM. - * @property timeToReady The time spent to load from the moment the [MediaItem][androidx.media3.common.MediaItem] became the current item until - * it started to play. + * Represents the timings spent in different stages of the loading process, until the current media started to play. + * + * @property source The time spent loading the media source. + * @property manifest The time spent loading the main manifest, if applicable. + * @property asset The time spent loading the asset. + * @property drm The time spent loading the DRM. + * @property timeToReady The total time elapsed from the moment the [MediaItem][androidx.media3.common.MediaItem] became the current item until it + * was ready to play. */ data class LoadDuration( val source: Duration? = null, From ac7c6b1ba71605b97c5f2fe0ccbdd8dc2a679755 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Muller?= Date: Thu, 21 Nov 2024 14:05:35 +0100 Subject: [PATCH 02/17] Review KDoc in `ch.srgssr.pillarbox.player.analytics` --- .../analytics/PillarboxAnalyticsCollector.kt | 4 +- .../analytics/PillarboxAnalyticsListener.kt | 43 +++++----- .../analytics/PlaybackSessionManager.kt | 84 ++++++++++--------- .../player/analytics/TotalPlaytimeCounter.kt | 18 ++-- 4 files changed, 78 insertions(+), 71 deletions(-) diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/analytics/PillarboxAnalyticsCollector.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/analytics/PillarboxAnalyticsCollector.kt index 910951650..f1d10d512 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/analytics/PillarboxAnalyticsCollector.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/analytics/PillarboxAnalyticsCollector.kt @@ -14,9 +14,7 @@ import ch.srgssr.pillarbox.player.asset.timeRange.Chapter import ch.srgssr.pillarbox.player.asset.timeRange.Credit /** - * Pillarbox analytics collector - * - * @constructor + * Collects and dispatches analytics events for Pillarbox player. * * @param clock The [Clock] used to generate timestamps. */ diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/analytics/PillarboxAnalyticsListener.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/analytics/PillarboxAnalyticsListener.kt index e716ec112..853fce62f 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/analytics/PillarboxAnalyticsListener.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/analytics/PillarboxAnalyticsListener.kt @@ -13,62 +13,67 @@ import ch.srgssr.pillarbox.player.asset.timeRange.Chapter import ch.srgssr.pillarbox.player.asset.timeRange.Credit /** - * Pillarbox analytics listener - * - * @see [AnalyticsListener] + * An interface for receiving analytics events from a [PillarboxPlayer]. */ interface PillarboxAnalyticsListener : AnalyticsListener { /** - * On smooth seeking enabled changed + * Called when the smooth seeking enabled state changes. * * @param eventTime The [EventTime]. - * @param smoothSeekingEnabled The new value of [PillarboxPlayer.smoothSeekingEnabled] + * @param smoothSeekingEnabled Whether smooth seeking is enabled. + * + * @see PillarboxPlayer.smoothSeekingEnabled */ fun onSmoothSeekingEnabledChanged(eventTime: EventTime, smoothSeekingEnabled: Boolean) {} /** - * On tracking enabled changed + * Called when the tracking enabled state changes. * * @param eventTime The [EventTime]. - * @param trackingEnabled The new value of [PillarboxPlayer.trackingEnabled] + * @param trackingEnabled Whether tracking is enabled. + * + * @see PillarboxPlayer.trackingEnabled */ fun onTrackingEnabledChanged(eventTime: EventTime, trackingEnabled: Boolean) {} /** - * `onChapterChanged` is called when either: - * - The player position changes while playing automatically. - * - The use seeks to a new position. + * Called when the current chapter changes. This can occur when either: + * - The player's position changes naturally during playback. + * - The user seeks to a new position. * - The playlist changes. * * @param eventTime The [EventTime]. - * @param chapter `null` when the current position is not in a chapter. + * @param chapter The active [Chapter], or `null` if the current position is not within a chapter. */ fun onChapterChanged(eventTime: EventTime, chapter: Chapter?) {} /** - * On blocked time range reached + * Called when the player reaches a blocked time range. * * @param eventTime The [EventTime]. - * @param blockedTimeRange The [BlockedTimeRange] reached by the player. + * @param blockedTimeRange The [BlockedTimeRange] that the player has entered. */ fun onBlockedTimeRangeReached(eventTime: EventTime, blockedTimeRange: BlockedTimeRange) {} /** - * `onCreditChanged` is called when either: - * - The player position changes while playing automatically. - * - The use seeks to a new position. + * Called when the current credit changes. This can occur when either: + * - The player's position changes naturally during playback. + * - The user seeks to a new position. * - The playlist changes. * * @param eventTime The [EventTime]. - * @param credit `null` when the current position is not in a Credit. + * @param credit The active [Credit], or `null` if the current position is not within a credit. */ fun onCreditChanged(eventTime: EventTime, credit: Credit?) {} /** - * A Stall occurs when the player is [Player.STATE_BUFFERING] after being [Player.STATE_READY] during playback without user interactions. + * Called when the player's stall state changes. + * + * A stall occurs when the player is buffering ([Player.STATE_BUFFERING]) after previously being in a ready state ([Player.STATE_READY]) during + * playback, and this change was not initiated by a user interaction (e.g., seeking). * * @param eventTime The [EventTime]. - * @param isStall true when the player is stalling. + * @param isStall Whether the player is currently stalling. */ fun onStallChanged(eventTime: EventTime, isStall: Boolean) {} diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/analytics/PlaybackSessionManager.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/analytics/PlaybackSessionManager.kt index 262cd5217..2502c8103 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/analytics/PlaybackSessionManager.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/analytics/PlaybackSessionManager.kt @@ -14,7 +14,6 @@ import androidx.media3.common.Timeline import androidx.media3.common.Timeline.EMPTY import androidx.media3.common.Timeline.Window import androidx.media3.exoplayer.ExoPlayer -import androidx.media3.exoplayer.analytics.AnalyticsListener import androidx.media3.exoplayer.analytics.AnalyticsListener.EventTime import androidx.media3.exoplayer.source.LoadEventInfo import androidx.media3.exoplayer.source.MediaLoadData @@ -25,34 +24,34 @@ import java.util.UUID import kotlin.time.Duration.Companion.milliseconds /** - * Playback session manager - * - * @constructor Create empty Playback session manager + * Manages playback sessions, representing interactions with individual [MediaItem]s. */ class PlaybackSessionManager { /** - * - A session is linked to the period inside the timeline, see [Timeline.getUidOfPeriod][androidx.media3.common.Timeline.getUidOfPeriod]. - * - A session is created when the player does something with a [MediaItem]. - * - A session is current if the media item associated with the session is the current [MediaItem]. - * - A session is destroyed when - * - It is no longer the current session. - * - It is removed from the player. - * - The player is released. + * Represents a playback session associated with a [MediaItem] in a [Timeline]. * - * @property periodUid The period id from [Timeline.getUidOfPeriod][androidx.media3.common.Timeline.getUidOfPeriod] for [mediaItem]. - * @property window The last known [Timeline.Window]. + * - A session is linked to the period inside the timeline, see [Timeline.getUidOfPeriod]. + * - A session is created when the player interacts with a [MediaItem]. + * - A session is considered the "current" session if its [mediaItem] is the [current `MediaItem`][Player.getCurrentMediaItem]. + * - A session is destroyed when: + * - It's no longer the current session (e.g., switching media items). + * - Its [mediaItem] is removed from the playlist. + * - The player is released. + * + * @property periodUid The id of the period in the timeline, obtained from [Timeline.getUidOfPeriod]. + * @property window The last known [Window] associated with this session. */ class Session( val periodUid: Any, val window: Window ) { /** - * Unique session id. + * Unique identifier for this session. */ val sessionId = UUID.randomUUID().toString() /** - * Media item + * The [MediaItem] associated with this session. */ val mediaItem: MediaItem = window.mediaItem @@ -80,10 +79,10 @@ class PlaybackSessionManager { } /** - * Session info + * Represents information about a session at a specific point in time. * - * @property session The [Session] - * @property position The position in milliseconds when a session change occurs. + * @property session The [Session]. + * @property position The position, in milliseconds, within a timeline when this session information is relevant. */ data class SessionInfo( val session: Session, @@ -91,29 +90,30 @@ class PlaybackSessionManager { ) /** - * Listener + * An interface for receiving notifications about session lifecycle events. */ interface Listener { /** - * On session created + * Called when a new session is created. * * @param session The newly created [Session]. */ fun onSessionCreated(session: Session) = Unit /** - * On session destroyed. The session won't be current anymore. + * Called when a session is destroyed. The session will no longer be the current session. * - * @param session The destroyed [Session]. + * @param session The [Session] that has been destroyed. */ fun onSessionDestroyed(session: Session) = Unit /** - * On current session changed from [oldSession] to [newSession]. - * [onSessionDestroyed] with [oldSession] is called right after. + * Called when the current session changes. + * + * Immediately following this call, [onSessionDestroyed] will be called with [oldSession] to signal the previous session's termination. * - * @param oldSession The current session, if any. - * @param newSession The next current session, if any. + * @param oldSession The previously active [Session], or `null` if there was none. + * @param newSession The newly active [Session], or `null` if there is none. */ fun onCurrentSessionChanged( oldSession: SessionInfo?, @@ -146,9 +146,9 @@ class PlaybackSessionManager { } /** - * Set the player + * Sets the [ExoPlayer] instance to monitor for managing sessions. * - * @param player + * @param player The [ExoPlayer] instance to be used. */ fun setPlayer(player: ExoPlayer) { currentTimeline = player.currentTimeline @@ -157,46 +157,47 @@ class PlaybackSessionManager { } /** - * Add listener + * Adds a listener to this session manager. * - * @param listener + * @param listener The listener to be added. */ fun addListener(listener: Listener) { listeners.add(listener) } /** - * Remove listener + * Removes a listener from this session manager. * - * @param listener + * @param listener The listener to be removed. */ fun removeListener(listener: Listener) { listeners.remove(listener) } /** - * Get current session + * Returns the current session. * - * @return + * @return The current session, or `null` if there is no active session. */ fun getCurrentSession(): Session? { return _currentSession } /** - * Get session from id + * Retrieves a session by its id. * - * @param sessionId - * @return + * @param sessionId The id of the session to retrieve. + * @return The session identified by [sessionId] if found, `null` otherwise. */ fun getSessionById(sessionId: String): Session? { return sessions.values.find { it.sessionId == sessionId } } /** - * Get session from event time + * Retrieves the [Session] associated with a given [EventTime]. * - * @param eventTime The [AnalyticsListener.EventTime]. + * @param eventTime The [EventTime] representing the event for which to retrieve the session. + * @return The [Session] associated with the event, or `null` if no session could be found. */ fun getSessionFromEventTime(eventTime: EventTime): Session? { if (eventTime.timeline.isEmpty) { @@ -209,9 +210,10 @@ class PlaybackSessionManager { } /** - * Get session from a period uid + * Retrieves the [Session] associated with a given period UID. * - * @param periodUid The period uid. + * @param periodUid The unique identifier of the period to retrieve the session for. + * @return The [Session] associated with the [periodUid], or `null` no session could be found. */ fun getSessionFromPeriodUid(periodUid: Any): Session? { return sessions[periodUid] diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/analytics/TotalPlaytimeCounter.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/analytics/TotalPlaytimeCounter.kt index 57c2faf04..a2d9a6ad1 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/analytics/TotalPlaytimeCounter.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/analytics/TotalPlaytimeCounter.kt @@ -9,9 +9,9 @@ import kotlin.time.Duration.Companion.ZERO import kotlin.time.Duration.Companion.milliseconds /** - * Total playtime counter. + * A class for tracking the total playtime of something. * - * @param timeProvider A callback invoked whenever the current time is needed. + * @param timeProvider A function that returns the current time, in milliseconds. */ class TotalPlaytimeCounter internal constructor( private val timeProvider: () -> Long, @@ -24,7 +24,7 @@ class TotalPlaytimeCounter internal constructor( ) /** - * Reset total playtime to zero + * Resets the total playtime counter to zero. */ fun reset() { totalPlayTime = ZERO @@ -32,8 +32,9 @@ class TotalPlaytimeCounter internal constructor( } /** - * Play - * Calling twice play after sometime will compute totalPlaytime + * Starts or resumes playback. + * + * Calling this function after a previous call to [play] will automatically calculate the accumulated playtime. */ fun play() { pause() @@ -41,9 +42,10 @@ class TotalPlaytimeCounter internal constructor( } /** - * Get total play time + * Calculates the total play time. * - * @return if paused totalPlayTime else totalPlayTime + delta from last play + * @return Either the accumulated total play time if paused, or the total play time plus the time elapsed since the last call to [play] if + * currently playing. */ fun getTotalPlayTime(): Duration { return if (lastPlayTime <= 0L) { @@ -54,7 +56,7 @@ class TotalPlaytimeCounter internal constructor( } /** - * Pause total play time tracking and compute total playtime. + * Pauses the tracking of total play time. */ fun pause() { if (lastPlayTime > 0L) { From 5d95214eb9380fe44de532dc5189a705820c693c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Muller?= Date: Thu, 21 Nov 2024 16:54:17 +0100 Subject: [PATCH 03/17] Review KDoc in `ch.srgssr.pillarbox.player.asset.timeRange` --- .../player/asset/timeRange/BlockedTimeRange.kt | 7 ++++--- .../pillarbox/player/asset/timeRange/Chapter.kt | 13 ++++++++----- .../pillarbox/player/asset/timeRange/Credit.kt | 6 +++--- .../player/asset/timeRange/TimeRange.kt | 16 ++++++++++------ 4 files changed, 25 insertions(+), 17 deletions(-) diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/asset/timeRange/BlockedTimeRange.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/asset/timeRange/BlockedTimeRange.kt index b49b36c1b..ad1adeb47 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/asset/timeRange/BlockedTimeRange.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/asset/timeRange/BlockedTimeRange.kt @@ -8,12 +8,13 @@ import android.os.Parcelable import kotlinx.parcelize.Parcelize /** - * Blocked time range. The player will always seek to [end] when reaching [start], regardless of the [reason] or [id]. + * Represents a time range that is blocked for playback. When the player reaches the [start] time of a blocked range, it will immediately seek to the + * [end] time, effectively skipping the blocked portion. This behavior is enforced regardless of the [reason] or [id] associated with the block. * * @property start The start position, in milliseconds. * @property end The end position, in milliseconds. - * @property reason The optional block reason. - * @property id The optional id. + * @property reason An optional string describing the reason for the block. + * @property id An optional unique identifier for the block. */ @Parcelize data class BlockedTimeRange( diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/asset/timeRange/Chapter.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/asset/timeRange/Chapter.kt index 37507bc96..e04718ecc 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/asset/timeRange/Chapter.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/asset/timeRange/Chapter.kt @@ -13,12 +13,15 @@ import kotlinx.parcelize.Parcelize import kotlinx.parcelize.TypeParceler /** - * Chapter + * Represents a chapter. * - * @property id The id of the chapter. - * @property start The start position, in milliseconds. - * @property end The end position, in milliseconds. - * @property mediaMetadata The [MediaMetadata]. + * A chapter is a segment of a media item defined by a start and end time. + * It can also contain associated metadata, such as a title or image. + * + * @property id The unique identifier of the chapter. + * @property start The start position of the chapter, in milliseconds. + * @property end The end position of the chapter, in milliseconds. + * @property mediaMetadata The [MediaMetadata] associated with the chapter. */ @Parcelize data class Chapter( diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/asset/timeRange/Credit.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/asset/timeRange/Credit.kt index 28ade8f9c..2afddd72e 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/asset/timeRange/Credit.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/asset/timeRange/Credit.kt @@ -8,11 +8,11 @@ import android.os.Parcelable import kotlinx.parcelize.Parcelize /** - * Credit + * Represents an opening or a closing credit. */ sealed interface Credit : TimeRange, Parcelable { /** - * Opening credits + * Represents the opening credits of a media. */ @Parcelize data class Opening( @@ -21,7 +21,7 @@ sealed interface Credit : TimeRange, Parcelable { ) : Credit /** - * Closing credits + * Represents the closing credits of a media. */ @Parcelize data class Closing( diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/asset/timeRange/TimeRange.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/asset/timeRange/TimeRange.kt index 85d830232..7d5ee2426 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/asset/timeRange/TimeRange.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/asset/timeRange/TimeRange.kt @@ -8,7 +8,7 @@ import androidx.media3.common.C import kotlin.math.abs /** - * Time range + * Represents a time range within a media playback timeline. */ sealed interface TimeRange { /** @@ -22,7 +22,7 @@ sealed interface TimeRange { val end: Long /** - * Duration, in milliseconds. + * The duration of the time range, in milliseconds. */ val duration: Long get() { @@ -30,10 +30,10 @@ sealed interface TimeRange { } /** - * Check if the provided [position][positionMs] is in this [TimeRange]. + * Checks if the provided [position][positionMs], in milliseconds, is within this [TimeRange]. * - * @param positionMs The position, in milliseconds. - * @return `true` if [positionMs] is between [start] (included) and [end] (excluded). + * @param positionMs The position to check, in milliseconds. + * @return Whether the [positionMs] is between [start] (inclusive) and [end] (exclusive). */ operator fun contains(positionMs: Long): Boolean { return positionMs in start.. List.firstOrNullAtPosition(position: Long): T? { return if (position == C.TIME_UNSET) { From e3e1c95c4d1ab00770a151198606e6c42ec16301 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Muller?= Date: Mon, 25 Nov 2024 17:04:05 +0100 Subject: [PATCH 04/17] Review KDoc in `ch.srgssr.pillarbox.player.asset` --- .../ch/srgssr/pillarbox/player/asset/Asset.kt | 10 +++++----- .../pillarbox/player/asset/AssetLoader.kt | 17 ++++++++--------- .../pillarbox/player/asset/UrlAssetLoader.kt | 5 +++-- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/asset/Asset.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/asset/Asset.kt index c4adb07ae..3535890b4 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/asset/Asset.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/asset/Asset.kt @@ -11,12 +11,12 @@ import ch.srgssr.pillarbox.player.tracker.MediaItemTrackerData import ch.srgssr.pillarbox.player.tracker.MutableMediaItemTrackerData /** - * Assets + * Represents an asset, which contains the necessary components for playback. * - * @property mediaSource The [MediaSource] used by the player to play something. - * @property trackersData The [MediaItemTrackerData]. - * @property mediaMetadata The [MediaMetadata] to set to the player media item. - * @property blockedTimeRanges The [BlockedTimeRange] list. + * @property mediaSource The [MediaSource] used by the player to play the content. + * @property trackersData The [MediaItemTrackerData] containing information for tracking playback events and metrics. + * @property mediaMetadata The [MediaMetadata] providing descriptive information about the media item, such as title, artist, etc. + * @property blockedTimeRanges A list of [BlockedTimeRange] instances representing periods within the media that should be skipped during playback. */ data class Asset( val mediaSource: MediaSource, diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/asset/AssetLoader.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/asset/AssetLoader.kt index 0f93969e5..5fa3a2e3b 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/asset/AssetLoader.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/asset/AssetLoader.kt @@ -8,25 +8,24 @@ import androidx.media3.common.MediaItem import androidx.media3.exoplayer.source.MediaSource /** - * Asset loader + * An abstract class responsible for loading an [Asset] from a given [MediaItem]. * - * @property mediaSourceFactory - * @constructor Create empty Asset loader + * @property mediaSourceFactory A factory for creating [MediaSource] instances. */ abstract class AssetLoader(val mediaSourceFactory: MediaSource.Factory) { /** - * Can load asset + * Determines if this [AssetLoader] is capable of loading an [Asset] from the provided [MediaItem]. * - * @param mediaItem The input [MediaItem]. - * @return true if this AssetLoader can load an Asset from the mediaItem. + * @param mediaItem The [MediaItem] representing the asset to potentially load. + * @return Whether this [AssetLoader] can load an [Asset] from the provided [mediaItem]. */ abstract fun canLoadAsset(mediaItem: MediaItem): Boolean /** - * Load asset + * Loads an asset based on the provided [MediaItem]. * - * @param mediaItem The input [MediaItem] - * @return a [Asset]. + * @param mediaItem The [MediaItem] describing the media to load. + * @return An [Asset] representing the loaded resource. */ abstract suspend fun loadAsset(mediaItem: MediaItem): Asset } diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/asset/UrlAssetLoader.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/asset/UrlAssetLoader.kt index abfc19ad3..0fa4e442d 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/asset/UrlAssetLoader.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/asset/UrlAssetLoader.kt @@ -6,11 +6,12 @@ package ch.srgssr.pillarbox.player.asset import androidx.media3.common.MediaItem import androidx.media3.exoplayer.source.DefaultMediaSourceFactory +import androidx.media3.exoplayer.source.MediaSource /** - * [AssetLoader] to load an [Asset] from a stream url. + * An [AssetLoader] implementation that loads an [Asset] from a URL provided in the [MediaItem]. * - * @param defaultMediaSourceFactory The [DefaultMediaSourceFactory] to create a MediaSource for the player. + * @param defaultMediaSourceFactory The [DefaultMediaSourceFactory] used to create a [MediaSource] for the player. */ class UrlAssetLoader( defaultMediaSourceFactory: DefaultMediaSourceFactory, From f76b4461d6a8e6c6a0f4680d5b65ddeea7eba6d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Muller?= Date: Tue, 26 Nov 2024 15:21:39 +0100 Subject: [PATCH 05/17] Review KDoc in `ch.srgssr.pillarbox.player.extension` --- .../pillarbox/player/extension/Format.kt | 39 ++++++--- .../player/extension/MediaMetadata.kt | 26 ++++-- .../pillarbox/player/extension/Player.kt | 43 ++++++---- .../player/extension/PlayerCommands.kt | 37 ++++++-- .../player/extension/PlayerTracks.kt | 4 +- .../extension/TrackSelectionParameters.kt | 84 +++++++++---------- .../pillarbox/player/extension/Tracks.kt | 12 ++- .../pillarbox/player/extension/VideoSize.kt | 10 ++- 8 files changed, 156 insertions(+), 99 deletions(-) diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/Format.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/Format.kt index 240a6b7d3..70ae59e08 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/Format.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/Format.kt @@ -12,32 +12,39 @@ import androidx.media3.common.VideoSize import java.util.Locale /** - * Check if [Format.roleFlags] contains [role] + * Checks if this [Format] has the specified [role]. * - * @param role The [C.RoleFlags] to check. + * @param role The role to check for, represented by a [C.RoleFlags] value. + * @return Whether this [Format] has the specified role. */ fun Format.hasRole(role: @RoleFlags Int): Boolean { return roleFlags and role != 0 } /** - * Check if [Format.selectionFlags] contains [selection] + * Checks if this [Format] has the specified [selection flags][selection]. * - * @param selection The [C.SelectionFlags] to check. + * @param selection The selection flags to check for, represented by a combination of values from [C.SelectionFlags]. + * @return Whether this [Format] has the specified selection flags. */ fun Format.hasSelection(selection: @SelectionFlags Int): Boolean { return selectionFlags and selection != 0 } /** - * Is forced + * Checks if this [Format] is forced. + * + * @return Whether this [Format] is forced. + * @see C.SELECTION_FLAG_FORCED */ fun Format.isForced(): Boolean { return hasSelection(C.SELECTION_FLAG_FORCED) } /** - * Video size of the format. [VideoSize.UNKNOWN] if no video size provided. + * Returns the video size of this [Format]. + * + * @return The video size of the format or [VideoSize.UNKNOWN] if not available. */ val Format.videoSize: VideoSize get() { @@ -48,7 +55,9 @@ val Format.videoSize: VideoSize } /** - * Selection flags as string + * Returns a string representation of the selection flags associated with this [Format]. + * + * @return A string representation of the selection flags, or an empty string if none are set. */ fun Format.selectionString(): String { val selectionFlags = mutableListOf() @@ -65,7 +74,9 @@ fun Format.selectionString(): String { } /** - * Role flags as string + * Returns a string representation of the role flags set for this [Format]. + * + * @return A string representing the role flags, or an empty string if none are set. */ @Suppress("CyclomaticComplexMethod") fun Format.roleString(): String { @@ -122,23 +133,27 @@ fun Format.roleString(): String { } /** - * Has accessibility roles + * Checks if this [Format] has accessibility roles, specifically if it describes video or sound. + * + * @return Whether this [Format] has accessibility roles. */ fun Format.hasAccessibilityRoles(): Boolean { return hasRole(C.ROLE_FLAG_DESCRIBES_VIDEO or C.ROLE_FLAG_DESCRIBES_MUSIC_AND_SOUND) } /** - * Returns a locale for the specified IETF BCP 47 [Format.language] tag string. + * Returns a [Locale] representing the language specified by this [Format]'s [language][Format.language] property. * - * @return null if not applicable. + * @return A [Locale] corresponding to the [language][Format.language] tag, or `null` if not available. */ fun Format.getLocale(): Locale? { return language?.let { Locale.forLanguageTag(it) } } /** - * Display name + * The display name for this [Format]. + * + * @return The display name of this [Format]. */ val Format.displayName: String get() = getLocale()?.displayName ?: label ?: C.LANGUAGE_UNDETERMINED diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/MediaMetadata.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/MediaMetadata.kt index 3071e1cc5..fb6307f9e 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/MediaMetadata.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/MediaMetadata.kt @@ -12,30 +12,40 @@ import ch.srgssr.pillarbox.player.asset.timeRange.Chapter import ch.srgssr.pillarbox.player.asset.timeRange.Credit /** - * Chapters + * A list of [Chapter]s for this media item. + * + * @return A list of [Chapter]s for this media item, or `null` if there are no chapters. */ val MediaMetadata.chapters: List? get() = getExtra(KeyChapters) /** - * Sets the [MediaMetadata.chapters]. - * Calling [MediaMetadata.Builder.setExtras] after will reset this call. - * @param chapters The list of [Chapter]. + * Sets the [Chapter]s. + * + * **Note:** calling [MediaMetadata.Builder.setExtras] after this function will reset the chapters information. + * + * @param chapters The list of [Chapter]s. + * @return This [MediaMetadata.Builder] instance for method chaining. */ fun MediaMetadata.Builder.setChapters(chapters: List): MediaMetadata.Builder { return setExtra(KeyChapters, chapters) } /** - * Credits + * A list of [Credit]s for this media item. + * + * @return A list of [Credit]s for this media item, or `null` if there are no credits. */ val MediaMetadata.credits: List? get() = getExtra(KeyCredits) /** - * Sets the [MediaMetadata.credits] - * Calling [MediaMetadata.Builder.setExtras] after will reset this call. - * @param credits The list of [Credit]. + * Sets the [Credit]s. + * + * **Note:** calling [MediaMetadata.Builder.setExtras] after this function will reset the credits information. + * + * @param credits The list of [Credit]s. + * @return This [MediaMetadata.Builder] instance for method chaining. */ fun MediaMetadata.Builder.setCredits(credits: List): MediaMetadata.Builder { return setExtra(KeyCredits, credits) diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/Player.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/Player.kt index 9ce042d6d..c6d8ba428 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/Player.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/Player.kt @@ -17,7 +17,9 @@ import kotlin.time.Duration.Companion.microseconds import kotlin.time.Duration.Companion.milliseconds /** - * Get a snapshot of the current media items + * Retrieves a snapshot of the current media items in the player. + * + * @return A list of [MediaItem], or an empty list if no items are set. */ fun Player.getCurrentMediaItems(): List { val count = mediaItemCount @@ -32,25 +34,26 @@ fun Player.getCurrentMediaItems(): List { } /** - * Get playback speed + * Returns the current playback speed of the player. * - * @return [Player.getPlaybackParameters] speed + * @return The current playback speed as a float value. */ fun Player.getPlaybackSpeed(): Float { return playbackParameters.speed } /** - * Current position percent + * Returns the current playback position as a percentage of the total duration. * - * @return the current position in percent [0,1]. + * @return The current playback position as a percentage, ranging from 0.0 to 1.0. */ fun Player.currentPositionPercentage(): Float { return currentPosition / duration.coerceAtLeast(1).toFloat() } /** - * Handle audio focus with the currently set [AudioAttributes][androidx.media3.common.AudioAttributes]. + * Sets whether the player should handle audio focus. + * * @param handleAudioFocus `true` if the player should handle audio focus, `false` otherwise. */ fun Player.setHandleAudioFocus(handleAudioFocus: Boolean) { @@ -58,45 +61,49 @@ fun Player.setHandleAudioFocus(handleAudioFocus: Boolean) { } /** - * @return The current media item chapters or an empty list. + * Returns the chapters for the currently playing media item. + * + * @return A list of [Chapter] for the currently playing media item, or an empty list if there are no chapters or no current media item. */ fun Player.getCurrentChapters(): List { return currentMediaItem?.mediaMetadata?.chapters ?: emptyList() } /** - * @return The current media item credits or an empty list. + * Returns the credits for the currently playing media item. + * + * @return A list of [Credit] for the currently playing media item, or an empty list if there are no credits or no current media item. */ fun Player.getCurrentCredits(): List { return currentMediaItem?.mediaMetadata?.credits.orEmpty() } /** - * Get the chapter at [position][positionMs]. + * Retrieves the [Chapter] that encompasses the given position in the media playback. * - * @param positionMs The position, in milliseconds, to find the chapter from. - * @return `null` if there is no chapter at [positionMs]. + * @param positionMs The position in the media playback, in milliseconds. + * @return The [Chapter] at the given position, or `null` if no chapter is found at that position. */ fun Player.getChapterAtPosition(positionMs: Long = currentPosition): Chapter? { return getCurrentChapters().firstOrNullAtPosition(positionMs) } /** - * Get the credit at [position][positionMs]. + * Retrieves the [Credit] that encompasses the given position in the media playback. * - * @param positionMs The position, in milliseconds, to find the credit from. - * @return `null` if there is no credit at [positionMs]. + * @param positionMs The position in the media playback, in milliseconds. + * @return The [Credit] at the given position, or `null` if no credit is found at that position. */ fun Player.getCreditAtPosition(positionMs: Long = currentPosition): Credit? { return getCurrentCredits().firstOrNullAtPosition(positionMs) } /** - * Is at live edge + * Checks if the current playback position is at the live edge of a live stream. * - * @param positionMs The position in milliseconds. - * @param window The optional Window. - * @return if [positionMs] is at live edge. + * @param positionMs The playback position, in milliseconds, to check. + * @param window A [Window] to store the current window information. + * @return Whether the playback position is at the live edge. */ fun Player.isAtLiveEdge(positionMs: Long = currentPosition, window: Window = Window()): Boolean { if (!isCurrentMediaItemLive) return false diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/PlayerCommands.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/PlayerCommands.kt index 63dec56ab..24252111f 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/PlayerCommands.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/PlayerCommands.kt @@ -4,66 +4,85 @@ */ package ch.srgssr.pillarbox.player.extension +import androidx.media3.common.MediaItem import androidx.media3.common.Player /** - * Can seek to a later position in the current or next MediaItem. + * Checks if the player can seek to a later position in the current or next [MediaItem]. + * + * @return Whether the player can seek to a later position. */ fun Player.Commands.canSeekToNext(): Boolean { return contains(Player.COMMAND_SEEK_TO_NEXT) } /** - * Can seek to an earlier position in the current or previous MediaItem. + * Checks if the player can seek to an earlier position in the current or previous [MediaItem]. + * + * @return Whether the player can seek to an earlier position. */ fun Player.Commands.canSeekToPrevious(): Boolean { return contains(Player.COMMAND_SEEK_TO_PREVIOUS) } /** - * Can seek back by a fixed increment into the current MediaItem. + * Checks if the player can seek forward by a fixed increment in the current [MediaItem]. + * + * @return Whether the player supports seeking forward. */ fun Player.Commands.canSeekForward(): Boolean { return contains(Player.COMMAND_SEEK_FORWARD) } /** - * Can seek back by a fixed increment into the current MediaItem. + * Checks if the player can seek back by a fixed increment in the current [MediaItem]. + * + * @return Whether the player supports seeking back. */ fun Player.Commands.canSeekBack(): Boolean { return contains(Player.COMMAND_SEEK_BACK) } /** - * Can seek anywhere into the current MediaItem. + * Checks if the player can seek in the current [MediaItem]. + * + * @return Whether the player supports seeking. */ fun Player.Commands.canSeek(): Boolean { return contains(Player.COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM) } /** - * Can start, pause or resume playback. + * Checks if the player can play/pause/resume the current [MediaItem]. + * + * @return Whether the player can play/pause/resume. */ fun Player.Commands.canPlayPause(): Boolean { return contains(Player.COMMAND_PLAY_PAUSE) } /** - * Can get details of the current track selection. + * Checks if the player can get the tracks in the current [MediaItem]. + * + * @return Whether the player can get the tracks. */ fun Player.Commands.canGetTracks(): Boolean { return contains(Player.COMMAND_GET_TRACKS) } /** - * set the player's track selection parameters. + * Checks if the player can set track selection parameters. + * + * @return Whether the player can set track selection parameters. */ fun Player.Commands.canSetTrackSelectionParameters(): Boolean { return contains(Player.COMMAND_SET_TRACK_SELECTION_PARAMETERS) } /** - * Can set the playback speed and pitch. + * Checks if the player can set the playback speed and pitch of the current [MediaItem]. + * + * @return Whether the player can set the playback speed and pitch. */ fun Player.Commands.canSpeedAndPitch(): Boolean { return contains(Player.COMMAND_SET_SPEED_AND_PITCH) diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/PlayerTracks.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/PlayerTracks.kt index 4e1ae62b4..4496beb56 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/PlayerTracks.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/PlayerTracks.kt @@ -8,9 +8,9 @@ import androidx.media3.common.Player import androidx.media3.common.TrackSelectionOverride /** - * Set track override. + * Sets a track selection override. * - * @param override + * @param override The track selection override to apply. */ fun Player.setTrackOverride(override: TrackSelectionOverride) { trackSelectionParameters = trackSelectionParameters.setTrackOverride(override) diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/TrackSelectionParameters.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/TrackSelectionParameters.kt index 5d544775e..f6aca98ca 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/TrackSelectionParameters.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/TrackSelectionParameters.kt @@ -17,29 +17,29 @@ import androidx.media3.common.TrackSelectionOverride import androidx.media3.common.TrackSelectionParameters /** - * Is text track disabled + * Indicates whether text track is disabled. */ val TrackSelectionParameters.isTextTrackDisabled: Boolean get() = disabledTrackTypes.contains(C.TRACK_TYPE_TEXT) || isForcedTextTrackActive /** - * Is audio track disabled + * Indicates whether audio track is disabled. */ val TrackSelectionParameters.isAudioTrackDisabled: Boolean get() = disabledTrackTypes.contains(C.TRACK_TYPE_AUDIO) /** - * Is video track disabled + * Indicates whether video track is disabled. */ val TrackSelectionParameters.isVideoTrackDisabled: Boolean get() = disabledTrackTypes.contains(C.TRACK_TYPE_VIDEO) /** - * Get overrides for track type + * Filters the existing track selection overrides and returns only those that apply to the specified track type. * - * @param trackType The track type to filter. - * @return + * @param trackType The type of track to filter for. + * @return A map containing only the track selection overrides that apply to the specified `trackType`. */ fun TrackSelectionParameters.getOverridesForTrackType( trackType: @TrackType Int @@ -48,10 +48,10 @@ fun TrackSelectionParameters.getOverridesForTrackType( } /** - * Has track override + * Checks if there is an override for the specified track type. * - * @param trackType The track type to filter. - * @return + * @param trackType The type of track to check for overrides. + * @return Whether there is at least one override for the specified track type. */ fun TrackSelectionParameters.hasTrackOverride(trackType: @TrackType Int): Boolean { if (overrides.isEmpty()) return false @@ -59,9 +59,9 @@ fun TrackSelectionParameters.hasTrackOverride(trackType: @TrackType Int): Boolea } /** - * Enable text track. + * Creates a new [TrackSelectionParameters] instance with text track enabled. * - * @return + * @return A new [TrackSelectionParameters] instance with text track enabled. */ fun TrackSelectionParameters.enableTextTrack(): TrackSelectionParameters { return buildUpon() @@ -74,27 +74,27 @@ fun TrackSelectionParameters.enableTextTrack(): TrackSelectionParameters { } /** - * Enable audio track. + * Creates a new [TrackSelectionParameters] instance with audio track enabled. * - * @return + * @return A new [TrackSelectionParameters] instance with audio track enabled. */ fun TrackSelectionParameters.enableAudioTrack(): TrackSelectionParameters { return enableTrackType(C.TRACK_TYPE_AUDIO) } /** - * Enable video track. + * Creates a new [TrackSelectionParameters] instance with video track enabled. * - * @return + * @return A new [TrackSelectionParameters] instance with video track enabled. */ fun TrackSelectionParameters.enableVideoTrack(): TrackSelectionParameters { return enableTrackType(C.TRACK_TYPE_VIDEO) } /** - * Disable text track + * Creates a new [TrackSelectionParameters] instance with text track disabled. * - * @return + * @return A new [TrackSelectionParameters] instance with text track disabled. */ fun TrackSelectionParameters.disableTextTrack(): TrackSelectionParameters { return buildUpon() @@ -106,28 +106,28 @@ fun TrackSelectionParameters.disableTextTrack(): TrackSelectionParameters { } /** - * Disable audio track + * Creates a new [TrackSelectionParameters] instance with audio track disabled. * - * @return + * @return A new [TrackSelectionParameters] instance with audio track disabled. */ fun TrackSelectionParameters.disableAudioTrack(): TrackSelectionParameters { return disableTrackType(C.TRACK_TYPE_AUDIO) } /** - * Disable video track + * Creates a new [TrackSelectionParameters] instance with video track disabled. * - * @return + * @return A new [TrackSelectionParameters] instance with video track disabled. */ fun TrackSelectionParameters.disableVideoTrack(): TrackSelectionParameters { return disableTrackType(C.TRACK_TYPE_VIDEO) } /** - * Default text track parameters. + * Returns a copy of this [TrackSelectionParameters] with default settings for text tracks. * - * @param context The context. - * @return + * @param context The [Context]. + * @return A new [TrackSelectionParameters] instance with default settings for text tracks. */ fun TrackSelectionParameters.defaultTextTrack(context: Context): TrackSelectionParameters { return buildUpon() @@ -141,12 +141,10 @@ fun TrackSelectionParameters.defaultTextTrack(context: Context): TrackSelectionP } /** - * Default audio track parameters. + * Returns a copy of this [TrackSelectionParameters] with default settings for audio tracks. * - * Reset [TrackSelectionParameters] for audio as Default. - * - * @param context The context. - * @return + * @param context The [Context]. + * @return A new [TrackSelectionParameters] instance with default settings for audio tracks. */ fun TrackSelectionParameters.defaultAudioTrack(context: Context): TrackSelectionParameters { return buildUpon() @@ -160,11 +158,9 @@ fun TrackSelectionParameters.defaultAudioTrack(context: Context): TrackSelection } /** - * Default video track parameters. - * - * Reset [TrackSelectionParameters] for video as Default. + * Returns a copy of this [TrackSelectionParameters] with default settings for video tracks. * - * @return + * @return A new [TrackSelectionParameters] instance with default settings for video tracks. */ fun TrackSelectionParameters.defaultVideoTrack(): TrackSelectionParameters { return buildUpon() @@ -177,14 +173,16 @@ fun TrackSelectionParameters.defaultVideoTrack(): TrackSelectionParameters { } /** - * Set track selection override + * Applies a [TrackSelectionOverride] to the current [TrackSelectionParameters]. * - * - Audio track selection override setups the preferred audio language to handle forced subtitles correctly. - * - Text track selection override setups the preferred text language. - * - Video track selection override setups the max video size. + * **Track Override Behavior:** * - * @param override The [TrackSelectionOverride] to apply. - * @return + * - **Audio:** sets the preferred audio language, which is crucial for handling forced subtitles correctly. + * - **Text:** sets the preferred text language and role flags based on the override. + * - **Video:** sets the maximum video size. + * + * @param override The [TrackSelectionOverride] containing the track selection criteria. + * @return A new [TrackSelectionParameters] instance with the override applied. */ fun TrackSelectionParameters.setTrackOverride(override: TrackSelectionOverride): TrackSelectionParameters { val builder = buildUpon() @@ -216,10 +214,12 @@ fun TrackSelectionParameters.setTrackOverride(override: TrackSelectionOverride): } /** - * Set preferred audio role flags to accessibility manager settings. + * Sets preferred audio role flags based on [AccessibilityManager] settings. + * + * This function does nothing on Android below Tiramisu or if the user has not enabled audio description in the accessibility settings. * - * Dos nothing for api level < 33 or when audio description request is off. - * @param context The context. + * @param context The [Context] used to access the [AccessibilityManager]. + * @return This [TrackSelectionParameters.Builder] for method chaining. */ fun TrackSelectionParameters.Builder.setPreferredAudioRoleFlagsToAccessibilityManagerSettings( context: Context diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/Tracks.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/Tracks.kt index 8b3f93777..b2bb30879 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/Tracks.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/Tracks.kt @@ -11,7 +11,9 @@ import ch.srgssr.pillarbox.player.source.PillarboxMediaSource import ch.srgssr.pillarbox.player.tracker.MediaItemTrackerData /** - * @return [MediaItemTrackerData] if it exists, `null` otherwise + * Retrieves the [MediaItemTrackerData] associated with this [Tracks]. + * + * @return The [MediaItemTrackerData] if found, `null` otherwise. */ fun Tracks.getMediaItemTrackerDataOrNull(): MediaItemTrackerData? { return groups.firstOrNull { @@ -20,7 +22,9 @@ fun Tracks.getMediaItemTrackerDataOrNull(): MediaItemTrackerData? { } /** - * @return a list of [BlockedTimeRange] if it exists, `null` otherwise + * Retrieves the list of [BlockedTimeRange] associated with this [Tracks]. + * + * @return The list of [BlockedTimeRange] if found, `null` otherwise. */ @Suppress("UNCHECKED_CAST") fun Tracks.getBlockedTimeRangeOrNull(): List? { @@ -30,9 +34,9 @@ fun Tracks.getBlockedTimeRangeOrNull(): List? { } /** - * Contains image track + * Checks if this [Tracks] contains a track of type [image][C.TRACK_TYPE_IMAGE]. * - * @return `true` if there is a track of type [C.TRACK_TYPE_IMAGE], `false` otherwise + * @return Whether an image track is present. */ fun Tracks.containsImageTrack(): Boolean { return containsType(C.TRACK_TYPE_IMAGE) diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/VideoSize.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/VideoSize.kt index 096b3313d..461b6cc33 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/VideoSize.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/VideoSize.kt @@ -8,16 +8,18 @@ import android.util.Rational import androidx.media3.common.VideoSize /** - * Compute the aspect ratio, return `null` if the aspect ratio can't be computed. + * Computes the aspect ratio of the video. + * + * @return The aspect ratio as a float, or `null` if it cannot be computed. */ fun VideoSize.computeAspectRatioOrNull(): Float? { return if (height == 0 || width == 0) null else width * this.pixelWidthHeightRatio / height } /** - * Convert VideoSize to Rational that is useful for picture in picture + * Converts this [VideoSize] to a [Rational] representation, which is particularly useful for scenarios like picture-in-picture. * - * @return a [Rational] + * @return A [Rational] representing the aspect ratio of the [VideoSize]. */ fun VideoSize.toRational(): Rational { return if (this == VideoSize.UNKNOWN) { @@ -28,6 +30,6 @@ fun VideoSize.toRational(): Rational { } /** - * Rational One with a Rationale set to 1/1. + * Represents the rational number one (1/1). */ val RATIONAL_ONE = Rational(1, 1) From 8d278045dffc68398cd28901653e6839a5fbff0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Muller?= Date: Tue, 26 Nov 2024 16:39:17 +0100 Subject: [PATCH 06/17] Review KDoc in `ch.srgssr.pillarbox.player.monitoring.models` --- .../monitoring/models/ErrorMessageData.kt | 6 ++-- .../monitoring/models/EventMessageData.kt | 20 ++++++------- .../player/monitoring/models/Message.kt | 8 ++--- .../player/monitoring/models/MessageData.kt | 2 +- .../player/monitoring/models/Session.kt | 30 +++++++++---------- .../player/monitoring/models/Timings.kt | 11 +++---- 6 files changed, 39 insertions(+), 38 deletions(-) diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/monitoring/models/ErrorMessageData.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/monitoring/models/ErrorMessageData.kt index 53bc4c504..2fcb58206 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/monitoring/models/ErrorMessageData.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/monitoring/models/ErrorMessageData.kt @@ -10,13 +10,13 @@ import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable /** - * Represents a [Player][androidx.media3.common.Player] error to send to a monitoring server. + * Represents a [Player] error to send to a monitoring server. * - * @property duration The duration of the media being player, in milliseconds. + * @property duration The duration of the media being played, in milliseconds. * @property log The log associated with the error. * @property message The error message. * @property name The name of the error. - * @property position The position of the player when the error occurred, in milliseconds, or `null` if not available. + * @property position The playback position, in milliseconds, when the error occurred. * @property positionTimestamp The current player timestamp, as retrieved from the playlist. * @property url The last loaded url. */ diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/monitoring/models/EventMessageData.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/monitoring/models/EventMessageData.kt index e84f339f6..b7ab85c3a 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/monitoring/models/EventMessageData.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/monitoring/models/EventMessageData.kt @@ -13,15 +13,15 @@ import kotlinx.serialization.Serializable * @property bandwidth The device-measured network bandwidth, in bits per second. * @property bitrate The bitrate of the current stream, in bits per second. * @property bufferDuration The forward duration of the buffer, in milliseconds. - * @property duration The duration of the media being player, in milliseconds. + * @property duration The duration of the media being played, in milliseconds. * @property playbackDuration The duration of the playback, in milliseconds. - * @property position The position of the player, in milliseconds. + * @property position The current playback position of the player, in milliseconds. * @property positionTimestamp The current player timestamp, as retrieved from the playlist. - * @property stall The information about stalls. + * @property stall Information about stalls that have occurred during playback. * @property streamType The type of stream being played. - * @property url The URL of the stream. - * @property vpn `true` if a VPN is enabled, `false` otherwise, `null` if the status could not be determined. - * @property frameDrops The number of frame drops. + * @property url The URL of the stream being played. + * @property vpn Indicates whether a VPN is enabled, or if the status could not be determined. + * @property frameDrops The number of frame drops that have occurred during playback. */ @Serializable data class EventMessageData( @@ -40,7 +40,7 @@ data class EventMessageData( val frameDrops: Int, ) : MessageData { /** - * The type of stream (live or on demand). + * Represents the type of a media stream. */ @Suppress("UndocumentedPublicProperty") enum class StreamType { @@ -52,10 +52,10 @@ data class EventMessageData( } /** - * Information about stalls. + * Represents information about stalls that occur during playback. * - * @property count The number of stalls that have occurred, not as a result of a seek. - * @property duration The total duration of the stalls, in milliseconds. + * @property count The total number of stalls that have occurred, excluding stalls caused by explicit seeks. + * @property duration The accumulated duration of all stalls, in milliseconds. */ @Serializable data class Stall( diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/monitoring/models/Message.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/monitoring/models/Message.kt index d1568de8a..394165ff0 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/monitoring/models/Message.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/monitoring/models/Message.kt @@ -12,9 +12,9 @@ import kotlinx.serialization.Serializable * * @property data The data associated with the message. * @property eventName The name of the event. - * @property sessionId The session id. - * @property timestamp The current timestamp. - * @property version The version of the schema used in [data]. + * @property sessionId The unique identifier for the session during which the event occurred. + * @property timestamp The timestamp of when the event occurred, in milliseconds. + * @property version The version of the schema used for the [data] property. */ @Serializable data class Message( @@ -25,7 +25,7 @@ data class Message( val version: Int = 1, ) { /** - * The name of the event that triggered this monitoring message. + * Represents the name of the event that triggered a monitoring message. */ @Suppress("UndocumentedPublicProperty") enum class EventName { diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/monitoring/models/MessageData.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/monitoring/models/MessageData.kt index 38150b16b..a5435085c 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/monitoring/models/MessageData.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/monitoring/models/MessageData.kt @@ -7,7 +7,7 @@ package ch.srgssr.pillarbox.player.monitoring.models import kotlinx.serialization.Serializable /** - * Base interface for all monitoring message data. + * Represents the base interface for all data carried by monitoring messages. */ @Serializable sealed interface MessageData diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/monitoring/models/Session.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/monitoring/models/Session.kt index cc36c7d6a..caffd3fca 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/monitoring/models/Session.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/monitoring/models/Session.kt @@ -16,15 +16,15 @@ import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable /** - * Represents a monitoring session, which contains information about the device, current media, and player. + * Represents a monitoring session, which encapsulates information about the device, current media, player, and performance metrics. * - * @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 qoeTimings The metrics about the time needed to load the various media components, as experienced by the user. - * @property qosTimings The metrics about the time needed to load the various media components, during the preload phase. - * @property screen The information about the device screen. + * @property device Information about the device. + * @property media Information about the media being played. + * @property operatingSystem Information about the device's operating system. + * @property player Information about the player. + * @property qoeTimings Quality of Experience timings, representing user-perceived performance metrics related to media loading and playback. + * @property qosTimings Quality of Service timings, representing pre-playback performance metrics gathered during resource loading. + * @property screen Information about the device's screen. */ @Serializable data class Session( @@ -66,7 +66,7 @@ data class Session( ) /** - * Information about the device screen. + * Represents the information about a device's screen. * * @property height The height of the screen, in pixels. * @property width The width of the screen, in pixels. @@ -78,7 +78,7 @@ data class Session( ) /** - * Information about the operating system. + * Represents information about the operating system. * * @property name The name of the operating system. * @property version The version of the operating system. @@ -90,10 +90,10 @@ data class Session( ) /** - * Information about the player. + * Represents information about the player. * * @property name The name of the player. - * @property platform The platform of the player. + * @property platform The platform the player is using. * @property version The version of the player. */ @Serializable @@ -104,7 +104,7 @@ data class Session( ) /** - * Information about the media being played. + * Represents information about the media being played. * * @property assetUrl The URL of the asset. * @property id The id of the media. @@ -120,7 +120,7 @@ data class Session( ) /** - * Information about the device. + * Represents information about the device. * * @property id The unique identifier of the device. * @property model The model of the device. @@ -133,7 +133,7 @@ data class Session( val type: Type?, ) { /** - * The type of device. + * Represents the type of a device. */ @Suppress("UndocumentedPublicProperty") enum class Type { diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/monitoring/models/Timings.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/monitoring/models/Timings.kt index f24fa0395..cb373962f 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/monitoring/models/Timings.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/monitoring/models/Timings.kt @@ -4,19 +4,20 @@ */ package ch.srgssr.pillarbox.player.monitoring.models +import androidx.media3.common.MediaItem import kotlinx.serialization.Serializable /** - * Timings + * Contains data classes representing timings related to media playback. */ object Timings { /** - * Represents the timings until the current media started to play, as experienced by the user. + * Represents the Quality of Experience timings until the current media started to play, as experienced by the user. * * @property asset The time spent to load the asset, in milliseconds. * @property metadata The time spent to load the media source, in milliseconds. - * @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, in milliseconds. + * @property total The time spent to load the media from the moment the [MediaItem] became the current item until it started to play, in + * milliseconds. */ @Serializable data class QoE( @@ -26,7 +27,7 @@ object Timings { ) /** - * Represents the timings until the current media started to play, during the preload phase. + * Represents the Quality of Service timings for pre-playback performance metrics gathered during resource loading. * * @property asset The time spent to load the asset, in milliseconds. * @property drm The time spent to load the DRM, in milliseconds. From a327aeddc2f922b5c305eb82b11b9361a2337ef3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Muller?= Date: Tue, 26 Nov 2024 17:05:14 +0100 Subject: [PATCH 07/17] Review KDoc in `ch.srgssr.pillarbox.player.monitoring` --- .../monitoring/MonitoringMessageHandler.kt | 70 +++++++++++-------- 1 file changed, 42 insertions(+), 28 deletions(-) diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/monitoring/MonitoringMessageHandler.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/monitoring/MonitoringMessageHandler.kt index 55d1a7884..7836a6046 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/monitoring/MonitoringMessageHandler.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/monitoring/MonitoringMessageHandler.kt @@ -19,35 +19,36 @@ import kotlinx.coroutines.launch import java.net.URL /** - * Monitoring message handler + * Interface for handling monitoring messages. */ interface MonitoringMessageHandler { /** - * Send event + * Sends a monitoring event. * - * @param event + * @param event The event to be sent. */ fun sendEvent(event: Message) } /** - * Factory used to create instances of [MonitoringMessageHandler]. + * A factory interface responsible for creating instances of [MonitoringMessageHandler]. * - * @param Config The config used to create a new [MonitoringMessageHandler]. + * @param Config The type of configuration object used to initialize a new [MonitoringMessageHandler]. */ interface MonitoringMessageHandlerFactory { /** - * Create a new instance of [MonitoringMessageHandler] using the provided [config]. + * Creates a new instance of [MonitoringMessageHandler] configured with the provided [config]. * - * @param config The config used to create a new [MonitoringMessageHandler]. + * @param config The configuration used to initialize the [MonitoringMessageHandler]. + * @return A new instance of [MonitoringMessageHandler] configured according to the provided [config]. */ fun createMessageHandler(config: Config): MonitoringMessageHandler } /** - * Receiver for creating [Config] instances of a specific [type][MonitoringMessageHandlerType]. + * A factory class responsible for creating [Config] instances. * - * @param Config The config used to create a new [MonitoringMessageHandler]. + * @param Config The type of configuration object used to initialize a new [MonitoringMessageHandler]. */ @PillarboxDsl class MonitoringConfigFactory internal constructor() @@ -55,16 +56,17 @@ class MonitoringConfigFactory internal constructor() /** * Represents a specific type of [MonitoringMessageHandler]. * - * @param Config The config used to create a new [MonitoringMessageHandler]. - * @param Factory The factory used to create a new [MonitoringMessageHandler]. + * @param Config The type of configuration used to create a new [MonitoringMessageHandler]. + * @param Factory The type of factory responsible to create a new [MonitoringMessageHandler]. */ abstract class MonitoringMessageHandlerType> { protected abstract val messageHandlerFactory: Factory /** - * Helper method to create a new [MonitoringMessageHandler]. + * Creates a new [MonitoringMessageHandler] using the provided configuration. * - * @param createConfig The lambda used to create the [Config] for the desired [MonitoringMessageHandler]. + * @param createConfig A lambda that returns a [Config] object. + * @return A new instance of [MonitoringMessageHandler] configured according to the provided [createConfig] lambda. */ operator fun invoke(createConfig: MonitoringConfigFactory.() -> Config): MonitoringMessageHandler { val config = MonitoringConfigFactory().createConfig() @@ -74,20 +76,22 @@ abstract class MonitoringMessageHandlerType() { override val messageHandlerFactory = Factory /** * Returns the [MonitoringMessageHandler] instance. + * + * @return The [MonitoringMessageHandler] instance. */ operator fun invoke(): MonitoringMessageHandler { return MessageHandler } /** - * Factory for creating new [NoOp] handler type. + * A factory for creating instances of the [NoOp] message handler. */ object Factory : MonitoringMessageHandlerFactory { override fun createMessageHandler(config: Nothing): MonitoringMessageHandler { @@ -101,16 +105,16 @@ object NoOp : MonitoringMessageHandlerType() { } /** - * Monitoring message handler that logs each event in Logcat. + * A monitoring message handler that logs each event to Logcat. */ object Logcat : MonitoringMessageHandlerType() { override val messageHandlerFactory = Factory /** - * Config class for the [Logcat] handler type. + * Configuration class for the [Logcat] handler type. * - * @property tag The tag to use to log the events in Logcat. - * @property priority The priority of this message. + * @property tag The tag used to identify log messages in Logcat. + * @property priority The priority level of the log messages. */ class Config internal constructor( val tag: String, @@ -118,7 +122,11 @@ object Logcat : MonitoringMessageHandlerType() { ) /** - * Helper method to create a new [Config] instance. + * Creates a new [Config] instance for the [MonitoringConfigFactory]. + * + * @param tag The tag used to identify log messages in Logcat. + * @param priority The priority level of the log messages. + * @return A new [Config] instance with the specified configuration. */ @Suppress("UnusedReceiverParameter") fun MonitoringConfigFactory.config( @@ -132,7 +140,7 @@ object Logcat : MonitoringMessageHandlerType() { } /** - * Factory for creating new [Logcat] handler type. + * A factory for creating instances of the [Logcat] message handler. */ object Factory : MonitoringMessageHandlerFactory { override fun createMessageHandler(config: Config): MonitoringMessageHandler { @@ -154,17 +162,17 @@ object Logcat : MonitoringMessageHandlerType() { } /** - * Monitoring message handler that sends each event to a remote server. + * A monitoring message handler that sends each event to a remote server. */ object Remote : MonitoringMessageHandlerType() { override val messageHandlerFactory = Factory /** - * Config class for the [Remote] handler type. + * Configuration class for the [Remote] handler type. * - * @property endpointUrl The endpoint receiving monitoring messages. - * @property httpClient The [HttpClient] to use to send the events. - * @property coroutineScope The scope used to send the monitoring message. + * @property endpointUrl The URL of the endpoint responsible for receiving monitoring messages. + * @property httpClient The [HttpClient] instance used for transmitting events to the endpoint. + * @property coroutineScope The [CoroutineScope] which manages the coroutine responsible for sending monitoring messages. */ class Config internal constructor( val endpointUrl: URL, @@ -173,7 +181,13 @@ object Remote : MonitoringMessageHandlerType() { ) /** - * Helper method to create a new [Config] instance. + * Creates a new [Config] instance for the [MonitoringConfigFactory]. + * + * @param endpointUrl The URL of the endpoint responsible for receiving monitoring messages. + * @param httpClient The [HttpClient] instance used for transmitting events to the endpoint. + * @param coroutineScope The [CoroutineScope] which manages the coroutine responsible for sending monitoring messages. + * + * @return A new [Config] instance with the specified configuration. */ @Suppress("UnusedReceiverParameter") fun MonitoringConfigFactory.config( @@ -189,7 +203,7 @@ object Remote : MonitoringMessageHandlerType() { } /** - * Factory for creating new [Remote] handler type. + * A factory for creating instances of the [Remote] message handler. */ object Factory : MonitoringMessageHandlerFactory { override fun createMessageHandler(config: Config): MonitoringMessageHandler { From 703bb90ec4faf40892dec2ca064095cf2634bec1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Muller?= Date: Tue, 26 Nov 2024 17:09:16 +0100 Subject: [PATCH 08/17] Review KDoc in `ch.srgssr.pillarbox.player.network` --- .../srgssr/pillarbox/player/network/PillarboxHttpClient.kt | 6 +++--- .../ch/srgssr/pillarbox/player/network/PillarboxOkHttp.kt | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/network/PillarboxHttpClient.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/network/PillarboxHttpClient.kt index 240ba97c3..8f3b97bf9 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/network/PillarboxHttpClient.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/network/PillarboxHttpClient.kt @@ -15,7 +15,7 @@ import kotlinx.serialization.json.ClassDiscriminatorMode import kotlinx.serialization.json.Json /** - * Provide a Ktor [HttpClient] instance tailored for Pillarbox's needs. + * Provides a pre-configured Ktor [HttpClient] instance tailored for Pillarbox's specific needs. */ object PillarboxHttpClient { /** @@ -47,9 +47,9 @@ object PillarboxHttpClient { } /** - * Returns the [HttpClient] tailored for Pillarbox's needs. + * Provides access to the underlying [HttpClient] instance configured for Pillarbox. * - * @return A [HttpClient] instance. + * @return The [HttpClient] instance used by Pillarbox. */ operator fun invoke(): HttpClient { return httpClient diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/network/PillarboxOkHttp.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/network/PillarboxOkHttp.kt index 188b6c20e..935a3d811 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/network/PillarboxOkHttp.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/network/PillarboxOkHttp.kt @@ -10,7 +10,7 @@ import okhttp3.logging.HttpLoggingInterceptor import okhttp3.logging.HttpLoggingInterceptor.Level /** - * Provide a [OkHttpClient] instance tailored for Pillarbox's needs. + * Provides a singleton instance of [OkHttpClient] configured for Pillarbox's requirements. */ object PillarboxOkHttp { @@ -26,9 +26,9 @@ object PillarboxOkHttp { } /** - * Returns the [OkHttpClient] tailored for Pillarbox's needs. + * Provides access to the pre-configured [OkHttpClient] instance used by Pillarbox. * - * @return A [OkHttpClient] instance. + * @return The pre-configured [OkHttpClient] instance. */ operator fun invoke(): OkHttpClient { return okHttpClient From 41804185015bec66439fda0f8171512f17ece505 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Muller?= Date: Tue, 26 Nov 2024 17:21:24 +0100 Subject: [PATCH 09/17] Review KDoc in `ch.srgssr.pillarbox.player.notification` --- .../PillarboxMediaDescriptionAdapter.kt | 8 ++++---- .../PillarboxNotificationManager.kt | 17 +++++++++-------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/notification/PillarboxMediaDescriptionAdapter.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/notification/PillarboxMediaDescriptionAdapter.kt index 46f1a5b90..d453bf53a 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/notification/PillarboxMediaDescriptionAdapter.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/notification/PillarboxMediaDescriptionAdapter.kt @@ -21,11 +21,11 @@ import kotlinx.coroutines.launch import java.net.URL /** - * Pillarbox media description adapter + * A [MediaDescriptionAdapter] for Pillarbox. * - * @param pendingIntent [PendingIntent] to use when a user click the notification. - * @param context Context of the application. - * @param coroutineScope The [CoroutineScope] used to download image. + * @param pendingIntent The [PendingIntent] to use when the notification is clicked. + * @param context The [Context] of the application. + * @param coroutineScope The [CoroutineScope] used for loading artwork asynchronously. */ class PillarboxMediaDescriptionAdapter( private val pendingIntent: PendingIntent?, diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/notification/PillarboxNotificationManager.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/notification/PillarboxNotificationManager.kt index 62b83f510..8c3fd4d7c 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/notification/PillarboxNotificationManager.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/notification/PillarboxNotificationManager.kt @@ -10,16 +10,16 @@ import androidx.media3.session.MediaSession import androidx.media3.ui.PlayerNotificationManager /** - * Pillarbox notification manager + * This object provides a builder to easily create a [PlayerNotificationManager] pre-configured for use with Pillarbox. */ object PillarboxNotificationManager { /** - * Preconfigured [PlayerNotificationManager] for Pillarbox. + * A builder for creating a preconfigured [PlayerNotificationManager] tailored for Pillarbox. * - * @param context + * @param context The [Context]. * @param notificationId The id of the notification to be posted. Must be greater than 0. - * @param channelId The id of the notification channel of an existing notification channel or of the channel that should be automatically created. - * In the latter case, setChannelNameResourceId(int) needs to be called as well. + * @param channelId The id of the notification channel. This can be an existing channel or a new one to be created. If creating a new channel, + * ensure to call [setChannelNameResourceId][PlayerNotificationManager.Builder.setChannelNameResourceId] as well. */ class Builder( context: Context, @@ -29,11 +29,12 @@ object PillarboxNotificationManager { private var mediaSession: MediaSession? = null /** - * Set media session to link with the PlayerNotification. + * Links the player notification to a given [MediaSession]. * - * Don't call [setMediaDescriptionAdapter] when using this method. It won't have any effect otherwise. + * **Note:** don't call [setMediaDescriptionAdapter] after this method, otherwise it won't have any effect. * - * @param mediaSession + * @param mediaSession The [MediaSession] to link with the notification. + * @return This [Builder] instance for chaining. */ fun setMediaSession(mediaSession: MediaSession): Builder { this.mediaSession = mediaSession From dd30800d910ffd83efc12666ecb901365f35fa68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Muller?= Date: Tue, 26 Nov 2024 17:38:32 +0100 Subject: [PATCH 10/17] Review KDoc in `ch.srgssr.pillarbox.player.service` --- .../player/service/PlaybackService.kt | 63 +++++++++++++------ 1 file changed, 43 insertions(+), 20 deletions(-) diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/service/PlaybackService.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/service/PlaybackService.kt index ecca26740..b780b8b4c 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/service/PlaybackService.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/service/PlaybackService.kt @@ -4,15 +4,18 @@ */ package ch.srgssr.pillarbox.player.service +import android.app.Activity import android.app.Notification import android.app.PendingIntent import android.app.Service import android.content.Intent +import android.content.ServiceConnection import android.os.Binder import android.os.IBinder import androidx.core.app.ServiceCompat import androidx.media3.common.C import androidx.media3.common.util.NotificationUtil +import androidx.media3.session.MediaLibraryService import androidx.media3.session.MediaSession import androidx.media3.ui.PlayerNotificationManager import ch.srgssr.pillarbox.player.PillarboxExoPlayer @@ -20,26 +23,34 @@ import ch.srgssr.pillarbox.player.extension.setHandleAudioFocus import ch.srgssr.pillarbox.player.notification.PillarboxMediaDescriptionAdapter /** - * Playback service that handle background playback and Media notification for a *Player*. + * Playback service that handles background playback and media notification for a player. * - * Add this permission inside your manifest : + * **Permissions** * + * Add the following permissions to your `AndroidManifest.xml`: * ```xml - * - * - * + * + * * ``` - * And add your PlaybackService to the application manifest as follow : * + * **Service Declaration** + * + * Declare your [PlaybackService] in your `AndroidManifest.xml` as follows: * ```xml - * + * * ``` * - * Drawbacks : - * Then last ServiceConnection is unbind, it kills the service. Can happen if binding to service is done inside the Activity without - * orientationChanges. So each time user rotate, it's kills the service. + * **Limitations** + * + * - **Service Termination:** the service is stopped when the last [ServiceConnection] is unbound. This can occur, for example, if the binding is done + * within an [Activity] without handling orientation changes. Each rotation could potentially kill the service. + * - **External Service Integration:** the player is not seamlessly integrated with external services like Android Auto. For Android Auto, you would + * need to create a [MediaLibraryService]. * - * The player is not well integrated with external service like Android Auto. Has for AndroidAuto you have to create a MediaLibraryService. + * **Usage** + * + * Subclass this abstract class and implement the [pendingIntent] method to provide a [PendingIntent] for the [MediaSession]'s session activity. You + * can customize the notification by overriding the [createNotificationBuilder] and [onMediaSessionCreated] methods. */ abstract class PlaybackService : Service() { private val binder = ServiceBinder() @@ -56,9 +67,11 @@ abstract class PlaybackService : Service() { } /** - * Create notification builder, can be override to customize it. + * Creates a [PlayerNotificationManager.Builder] for building the notification. + * + * This method can be overridden to customize the notification's appearance and behavior. * - * @return + * @return A [PlayerNotificationManager.Builder] instance. */ open fun createNotificationBuilder(): PlayerNotificationManager.Builder { return PlayerNotificationManager.Builder(this, DEFAULT_NOTIFICATION_ID, DEFAULT_CHANNEL_ID) @@ -79,9 +92,9 @@ abstract class PlaybackService : Service() { } /** - * Set player to be connected to MediaNotification and MediaSession. + * Sets the player to be connected to MediaNotification and [MediaSession]. * - * @param player Player to be linked with this PlaybackService + * @param player The [PillarboxExoPlayer] instance to be linked with this [PlaybackService]. */ fun setPlayer(player: PillarboxExoPlayer) { if (this.player != player) { @@ -104,7 +117,14 @@ abstract class PlaybackService : Service() { } /** - * Allow [MediaSession.Builder] customization except [MediaSession.Builder.setSessionActivity] + * Called when the [MediaSession] is being created, allowing for customization of the [MediaSession.Builder]. + * + * **Note:** customization of [setSessionActivity][MediaSession.Builder.setSessionActivity] is not allowed through this method. The session + * activity is determined by the [pendingIntent] provided during initialization. + * + * @param mediaSessionBuilder The builder for the [MediaSession]. + * @return The modified [MediaSession.Builder]. + * * @see pendingIntent */ open fun onMediaSessionCreated(mediaSessionBuilder: MediaSession.Builder): MediaSession.Builder { @@ -112,7 +132,10 @@ abstract class PlaybackService : Service() { } /** - * Pending intent for [MediaSession.getSessionActivity] + * Returns a [PendingIntent] that will be used to launch an [Activity] specified by [MediaSession.setSessionActivity] when the user interacts with + * a media notification. + * + * @return A [PendingIntent] to launch the session [Activity]. */ abstract fun pendingIntent(): PendingIntent @@ -121,13 +144,13 @@ abstract class PlaybackService : Service() { } /** - * Service binder to set Player + * A [Binder] class for interacting with the [PlaybackService]. */ inner class ServiceBinder : Binder() { /** - * Set [player] linked to this service [MediaSession] and to be handled for background playback. + * Sets the [player] to be used by this [MediaSession] for background playback. * - * @param player + * @param player The [PillarboxExoPlayer] instance to be linked to the [MediaSession]. */ fun setPlayer(player: PillarboxExoPlayer) { this@PlaybackService.setPlayer(player) From c265da671eea0b6704f4aa34b4a28eeab7593c20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Muller?= Date: Tue, 26 Nov 2024 18:14:42 +0100 Subject: [PATCH 11/17] Review KDoc in `ch.srgssr.pillarbox.player.source` --- .../player/source/PillarboxMediaSource.kt | 26 +++++++++---------- .../source/PillarboxMediaSourceFactory.kt | 23 ++++++++-------- 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/source/PillarboxMediaSource.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/source/PillarboxMediaSource.kt index bced36b6b..60a3bf703 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/source/PillarboxMediaSource.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/source/PillarboxMediaSource.kt @@ -10,7 +10,6 @@ import androidx.media3.common.Format import androidx.media3.common.MediaItem import androidx.media3.common.MimeTypes import androidx.media3.common.Timeline -import androidx.media3.common.TrackGroup import androidx.media3.datasource.DataSpec import androidx.media3.datasource.TransferListener import androidx.media3.exoplayer.source.CompositeMediaSource @@ -31,13 +30,14 @@ import kotlin.time.TimeMark import kotlin.time.TimeSource /** - * Pillarbox media source + * A custom [MediaSource] that wraps another [MediaSource] to provide: + * - Flexible asset loading via an [AssetLoader]. + * - Load event handling (started, completed, error). * - * @param mediaItem The [MediaItem] to used for the assetLoader. - * @param assetLoader The [AssetLoader] to used to load the source. - * @param minLiveDvrDurationMs Minimal duration in milliseconds to consider a live with seek capabilities. - * @param timeSource The [TimeSource]. - * @constructor Create empty Pillarbox media source + * @param mediaItem The [MediaItem] to load. + * @param assetLoader The [AssetLoader] used to load the asset. + * @param minLiveDvrDurationMs Minimum duration, in milliseconds, for a live stream to be considered seekable. + * @param timeSource The [TimeSource] for generating timestamps for load events. */ class PillarboxMediaSource internal constructor( private var mediaItem: MediaItem, @@ -83,12 +83,12 @@ class PillarboxMediaSource internal constructor( } /** - * Can update media item + * Checks whether the [MediaItem] can be updated without reloading the media source. * * TODO Test when using MediaController or MediaBrowser. * - * @param mediaItem The new mediaItem, this method is called when we replace media item. - * @return true if the media can be update without reloading the media source. + * @param mediaItem The new [MediaItem]. + * @return Whether the [MediaItem] can be updated without reloading the media source. */ override fun canUpdateMediaItem(mediaItem: MediaItem): Boolean { val currentItemWithoutTag = this.mediaItem.buildUpon().setTag(null).build() @@ -211,7 +211,7 @@ class PillarboxMediaSource internal constructor( @Suppress("UndocumentedPublicClass") companion object { /** - * Data type for SRG SSR assets. + * A data type for SRG SSR assets. */ const val DATA_TYPE_CUSTOM_ASSET = C.DATA_TYPE_CUSTOM_BASE + 1 private const val TAG = "PillarboxMediaSource" @@ -227,12 +227,12 @@ class PillarboxMediaSource internal constructor( internal const val PILLARBOX_BLOCKED_MIME_TYPE = "${MimeTypes.BASE_TYPE_APPLICATION}/pillarbox-blocked" /** - * [TrackGroup.type] for [Format]s with mime type [PILLARBOX_TRACKERS_MIME_TYPE]. + * This track type is used to identify tracks containing Pillarbox trackers data. */ const val TRACK_TYPE_PILLARBOX_TRACKERS = C.DATA_TYPE_CUSTOM_BASE + 1 /** - * [TrackGroup.type] for [Format]s with mime type [PILLARBOX_BLOCKED_MIME_TYPE]. + * This track type is used to identify tracks containing blocked segments. */ const val TRACK_TYPE_PILLARBOX_BLOCKED = TRACK_TYPE_PILLARBOX_TRACKERS + 1 diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/source/PillarboxMediaSourceFactory.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/source/PillarboxMediaSourceFactory.kt index 57a831b0a..594bf4fe6 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/source/PillarboxMediaSourceFactory.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/source/PillarboxMediaSourceFactory.kt @@ -18,18 +18,19 @@ import ch.srgssr.pillarbox.player.network.PillarboxOkHttp import kotlin.time.TimeSource /** - * Pillarbox media source factory create a new [PillarboxMediaSource] from a [MediaItem]. - * It selects the first [AssetLoader] to use by checking if [AssetLoader.canLoadAsset]. + * A factory for creating [PillarboxMediaSource] instances. * - * @param context to create the [defaultAssetLoader]. - * @param timeSource The [TimeSource]. + * This factory selects the first suitable [AssetLoader] to use for a given [MediaItem] by checking if [AssetLoader.canLoadAsset] returns `true`. + * + * @param context The [Context] used to create the default [AssetLoader]. + * @param timeSource The [TimeSource] to use for the created [MediaSource]. */ class PillarboxMediaSourceFactory( context: Context, private val timeSource: TimeSource = TimeSource.Monotonic ) : MediaSource.Factory { /** - * Default asset loader used when no other AssetLoader has been found. + * The default [AssetLoader] used to load assets when no other [AssetLoader] is able to handle the request. */ val defaultAssetLoader = UrlAssetLoader( DefaultMediaSourceFactory( @@ -41,16 +42,16 @@ class PillarboxMediaSourceFactory( ) /** - * Minimal duration in milliseconds to consider a live with seek capabilities. + * The minimum duration of the live stream, in milliseconds, for it to be considered a live stream with DVR capabilities. */ var minLiveDvrDurationMs = LIVE_DVR_MIN_DURATION_MS private val listAssetLoader = mutableListOf() /** - * Add asset loader + * Adds an [AssetLoader] at the specified index. * - * @param index index at which the specified element is to be inserted element – element to be inserted - * @param assetLoader [AssetLoader] to insert. + * @param index The index at which the [AssetLoader] should be added. + * @param assetLoader The [AssetLoader] to add. */ fun addAssetLoader(index: Int, assetLoader: AssetLoader) { check(assetLoader !is UrlAssetLoader) { "Already in the factory by default" } @@ -58,9 +59,9 @@ class PillarboxMediaSourceFactory( } /** - * Add asset loader + * Adds an [AssetLoader]. * - * @param assetLoader [AssetLoader] to insert. + * @param assetLoader The [AssetLoader] to add. */ fun addAssetLoader(assetLoader: AssetLoader) { check(assetLoader !is UrlAssetLoader) { "Already in the factory by default" } From 25032996203441016860704f7ac8e40c8293e82e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Muller?= Date: Wed, 27 Nov 2024 09:02:25 +0100 Subject: [PATCH 12/17] Review KDoc in `ch.srgssr.pillarbox.player.tracker` --- .../player/tracker/MediaItemTracker.kt | 22 ++++++++++++------- .../player/tracker/MediaItemTrackerData.kt | 20 +++++++++-------- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/tracker/MediaItemTracker.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/tracker/MediaItemTracker.kt index 16f17b47c..14a3680a9 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/tracker/MediaItemTracker.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/tracker/MediaItemTracker.kt @@ -7,32 +7,38 @@ package ch.srgssr.pillarbox.player.tracker import androidx.media3.exoplayer.ExoPlayer /** - * Media item tracker + * A tracker for media items played by an player. + * + * @param T The type of data associated with each tracked media item. */ interface MediaItemTracker { /** - * Start Media tracking. + * Initiates media tracking for the given player. * - * @param player The player to track. - * @param data The data associated. + * @param player The tracked [ExoPlayer] instance. + * @param data The data associated with the playback session. */ fun start(player: ExoPlayer, data: T) /** - * Stop Media tracking. + * Stops media tracking for the given player. * - * @param player The player tracked. The current player state may reflect the next item. + * @param player The tracked [ExoPlayer] instance. The player's current state may already reflect the next item. */ fun stop(player: ExoPlayer) /** - * Factory + * A factory interface for creating instances of [MediaItemTracker]. + * + * @param T The type of data associated with the created tracker. */ fun interface Factory { /** - * @return a new instance of a [MediaItemTracker] + * Creates a new instance of a [MediaItemTracker]. + * + * @return A new instance of a [MediaItemTracker]. */ fun create(): MediaItemTracker } diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/tracker/MediaItemTrackerData.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/tracker/MediaItemTrackerData.kt index 0e8187f0f..faf3b0eb0 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/tracker/MediaItemTrackerData.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/tracker/MediaItemTrackerData.kt @@ -5,36 +5,38 @@ package ch.srgssr.pillarbox.player.tracker /** - * Link between [data] and it's [factory]. + * Represents a pairing of a [MediaItemTracker.Factory] and its associated data. * - * @param T The factory data type. - * @property factory The [MediaItemTracker.Factory]. - * @property data The data of type T to use in [MediaItemTracker.start]. + * @param T The type of data used by the factory. + * @property factory The [MediaItemTracker.Factory] responsible for creating [MediaItemTracker]. + * @property data The data of type [T] that will be passed to the tracker's [start][MediaItemTracker.start] method. */ class FactoryData(val factory: MediaItemTracker.Factory, val data: T) /** - * Mutable MediaItem tracker data. + * Mutable representation of [MediaItemTrackerData] used to build and modify tracking data. * - * @constructor Create empty Mutable media item tracker data + * @constructor Creates an empty [MutableMediaItemTrackerData] instance. */ class MutableMediaItemTrackerData : MutableMap> by mutableMapOf() { /** - * To media item tracker data + * Converts this object to an immutable [MediaItemTrackerData] instance. + * + * @return A new [MediaItemTrackerData] instance populated with data from this object. */ fun toMediaItemTrackerData() = MediaItemTrackerData(this) @Suppress("UndocumentedPublicClass") companion object { /** - * Empty mutable media item tracker data. + * An empty instance of [MutableMediaItemTrackerData]. */ val EMPTY = MutableMediaItemTrackerData() } } /** - * Immutable MediaItem tracker data. + * Immutable snapshot of the [MediaItemTracker]'s [FactoryData]. */ class MediaItemTrackerData internal constructor( mutableMediaItemTrackerData: MutableMediaItemTrackerData From a74ecabf603d1a49471c5de1c5843c8279ad9559 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Muller?= Date: Wed, 27 Nov 2024 09:56:16 +0100 Subject: [PATCH 13/17] Review KDoc in `ch.srgssr.pillarbox.player.tracks` --- .../player/tracks/PlayerExtensions.kt | 24 +++++----- .../srgssr/pillarbox/player/tracks/Track.kt | 45 ++++++++++--------- .../player/tracks/TracksExtensions.kt | 9 ++-- 3 files changed, 40 insertions(+), 38 deletions(-) diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/tracks/PlayerExtensions.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/tracks/PlayerExtensions.kt index b5deddf87..eca6753cd 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/tracks/PlayerExtensions.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/tracks/PlayerExtensions.kt @@ -19,7 +19,7 @@ import ch.srgssr.pillarbox.player.extension.enableVideoTrack import ch.srgssr.pillarbox.player.extension.setTrackOverride /** - * Select the provided [track]. + * Selects the provided [track] for playback. * * @param track The [Track] to select. */ @@ -30,67 +30,67 @@ fun Player.selectTrack(track: Track) { } /** - * Enable the audio track. + * Enables the audio track. */ fun Player.enableAudioTrack() { trackSelectionParameters = trackSelectionParameters.enableAudioTrack() } /** - * Enable the text track. + * Enables the text track. */ fun Player.enableTextTrack() { trackSelectionParameters = trackSelectionParameters.enableTextTrack() } /** - * Enable the video track. + * Enables the video track. */ fun Player.enableVideoTrack() { trackSelectionParameters = trackSelectionParameters.enableVideoTrack() } /** - * Disable the audio track. + * Disables the audio track. */ fun Player.disableAudioTrack() { trackSelectionParameters = trackSelectionParameters.disableAudioTrack() } /** - * Disable the text track. + * Disables the text track. */ fun Player.disableTextTrack() { trackSelectionParameters = trackSelectionParameters.disableTextTrack() } /** - * Disable the video track. + * Disables the video track. */ fun Player.disableVideoTrack() { trackSelectionParameters = trackSelectionParameters.disableVideoTrack() } /** - * Restore the default audio track. + * Sets the track selection to automatically select the default audio track. * - * @param context + * @param context The [Context]. */ fun Player.setAutoAudioTrack(context: Context) { trackSelectionParameters = trackSelectionParameters.defaultAudioTrack(context) } /** - * Restore the default text track. + * Sets the track selection to automatically select the default text track. * - * @param context + * @param context The [Context]. */ fun Player.setAutoTextTrack(context: Context) { trackSelectionParameters = trackSelectionParameters.defaultTextTrack(context) } /** - * Restore the default video track. + * Sets the track selection to automatically select the default video track. */ fun Player.setAutoVideoTrack() { trackSelectionParameters = trackSelectionParameters.defaultVideoTrack() diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/tracks/Track.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/tracks/Track.kt index 55c528cda..739a157b4 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/tracks/Track.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/tracks/Track.kt @@ -11,11 +11,11 @@ import androidx.media3.common.Format import androidx.media3.common.Tracks /** - * Generic wrapper a of track. + * Represents a generic track within a media stream. * - * @property group The [Group][Tracks.Group] containing this [Track]. - * @property groupIndex The index of the containing [Group][Tracks.Group]. - * @property trackIndexInGroup The index of this [Track] in its containing [Group][Tracks.Group]. + * @property group The [Tracks.Group] that contains this track. + * @property groupIndex The index of the containing [Tracks.Group] within the overall track list. + * @property trackIndexInGroup The index of this track within its containing [Tracks.Group]. */ sealed class Track( internal val group: Tracks.Group, @@ -29,13 +29,13 @@ sealed class Track( get() = group.getTrackFormat(trackIndexInGroup) /** - * `true` if this [Track] is selected, `false` otherwise. + * Indicates whether this [Track] is currently selected. */ val isSelected: Boolean get() = group.isTrackSelected(trackIndexInGroup) /** - * `true` if this [Track] is supported, `false` otherwise. + * Indicates whether this [Track] is supported for playback. */ val isSupported: Boolean get() = group.isTrackSupported(trackIndexInGroup) @@ -43,11 +43,12 @@ sealed class Track( @Suppress("UndocumentedPublicClass") companion object { /** - * Converts the track at index [trackIndexInGroup] from the provided [group] into a [Track]. + * Creates a [Track] from the track at [trackIndexInGroup] in [group]. * - * @param group The [Group][Tracks.Group] containing the track to convert. - * @param groupIndex The index of the containing [Group][Tracks.Group]. - * @param trackIndexInGroup The index of this [Track] in its containing [Group][Tracks.Group]. + * @param group The [Tracks.Group] containing the track to convert. + * @param groupIndex The index of the containing [Tracks.Group]. + * @param trackIndexInGroup The index of the track within its containing [Tracks.Group]. + * @return A [Track] representing the desired track, or `null` if the [Tracks.Group.type] is not supported. */ operator fun invoke( group: Tracks.Group, @@ -71,11 +72,11 @@ sealed class Track( } /** - * Represent an audio track. + * Represents an audio track within a media file. * - * @param group The [Group][Tracks.Group] containing this [Track]. - * @param groupIndex The index of the containing [Group][Tracks.Group]. - * @param trackIndexInGroup The index of this [Track] in its containing [Group][Tracks.Group]. + * @param group The [Group][Tracks.Group] this audio track belongs to. + * @param groupIndex The index of the group this track belongs to within the overall track list. + * @param trackIndexInGroup The index of this track within its containing group. */ class AudioTrack( group: Tracks.Group, @@ -88,11 +89,11 @@ class AudioTrack( ) /** - * Represent a text track. + * Represents a text track within a media file. * - * @param group The [Group][Tracks.Group] containing this [Track]. - * @param groupIndex The index of the containing [Group][Tracks.Group]. - * @param trackIndexInGroup The index of this [Track] in its containing [Group][Tracks.Group]. + * @param group The [Group][Tracks.Group] this audio track belongs to. + * @param groupIndex The index of the group this track belongs to within the overall track list. + * @param trackIndexInGroup The index of this track within its containing group. */ class TextTrack( group: Tracks.Group, @@ -105,11 +106,11 @@ class TextTrack( ) /** - * Represent a video track. + * Represents a video track within a media file. * - * @param group The [Group][Tracks.Group] containing this [Track]. - * @param groupIndex The index of the containing [Group][Tracks.Group]. - * @param trackIndexInGroup The index of this [Track] in its containing [Group][Tracks.Group]. + * @param group The [Group][Tracks.Group] this audio track belongs to. + * @param groupIndex The index of the group this track belongs to within the overall track list. + * @param trackIndexInGroup The index of this track within its containing group. */ class VideoTrack( group: Tracks.Group, diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/tracks/TracksExtensions.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/tracks/TracksExtensions.kt index 60cf23d92..a2c31cbe9 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/tracks/TracksExtensions.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/tracks/TracksExtensions.kt @@ -5,12 +5,13 @@ package ch.srgssr.pillarbox.player.tracks import androidx.media3.common.C +import androidx.media3.common.MediaItem import androidx.media3.common.Tracks import ch.srgssr.pillarbox.player.extension.hasRole import ch.srgssr.pillarbox.player.extension.isForced /** - * All the supported tracks for the currently played [MediaItem][androidx.media3.common.MediaItem]. + * Returns a list of supported tracks for the currently played [MediaItem]. */ val Tracks.tracks: List get() = toTrackSequence() @@ -21,7 +22,7 @@ val Tracks.tracks: List .toList() /** - * All the supported audio tracks for the currently played [MediaItem][androidx.media3.common.MediaItem]. + * Returns a list of supported audio tracks for the currently played [MediaItem]. */ val Tracks.audioTracks: List get() = toTrackSequence() @@ -31,7 +32,7 @@ val Tracks.audioTracks: List .toList() /** - * All the supported text tracks for the currently played [MediaItem][androidx.media3.common.MediaItem]. + * Returns a list of supported text tracks for the currently played [MediaItem]. */ val Tracks.textTracks: List get() = toTrackSequence() @@ -42,7 +43,7 @@ val Tracks.textTracks: List .toList() /** - * All the supported video tracks for the currently played [MediaItem][androidx.media3.common.MediaItem]. + * Returns a list of supported video tracks for the currently played [MediaItem]. */ val Tracks.videoTracks: List get() = toTrackSequence() From 351c478b9a1a93634774d0e9bfd67523fd92d403 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Muller?= Date: Wed, 27 Nov 2024 10:36:58 +0100 Subject: [PATCH 14/17] Review KDoc in `ch.srgssr.pillarbox.player.utils` --- .../pillarbox/player/utils/BitrateUtil.kt | 10 ++++-- .../pillarbox/player/utils/DebugLogger.kt | 36 ++++++++++++------- .../pillarbox/player/utils/Heartbeat.kt | 20 ++++++----- .../player/utils/PendingIntentUtils.kt | 16 ++++++--- .../player/utils/PillarboxEventLogger.kt | 5 ++- .../pillarbox/player/utils/StringUtil.kt | 22 +++++++++--- 6 files changed, 71 insertions(+), 38 deletions(-) diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/utils/BitrateUtil.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/utils/BitrateUtil.kt index 90e3ae449..e9426f1e1 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/utils/BitrateUtil.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/utils/BitrateUtil.kt @@ -5,19 +5,23 @@ package ch.srgssr.pillarbox.player.utils /** - * Bitrate util + * Utility object providing functions for converting bit rates to byte rates. */ object BitrateUtil { /** - * @return Convert Int in bits rate to Int in byte rate. + * Converts a bit rate to a byte rate. + * + * @return The byte rate equivalent of the given bit rate. */ fun Int.toByteRate(): Int { return this / Byte.SIZE_BITS } /** - * @return Convert Long in bits rate to Long in byte rate. + * Converts a bit rate to a byte rate. + * + * @return The byte rate equivalent of the given bit rate. */ fun Long.toByteRate(): Long { return this / Byte.SIZE_BITS diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/utils/DebugLogger.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/utils/DebugLogger.kt index 2a8eb0c62..4f966f497 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/utils/DebugLogger.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/utils/DebugLogger.kt @@ -8,14 +8,18 @@ import android.util.Log import ch.srgssr.pillarbox.player.BuildConfig /** - * Debug logger use Android Log only if BuildConfig.DEBUG + * A utility class for logging debug messages. + * + * This logger only logs messages if Pillarbox is built in debug mode. */ object DebugLogger { /** - * @param tag tag to log - * @param message message to log - * @param throwable error to log + * Logs a debug message. + * + * @param tag The tag to associate with the log message. + * @param message The message to log. + * @param throwable An optional [Throwable] to include in the log message. */ fun debug(tag: String, message: String, throwable: Throwable? = null) { if (BuildConfig.DEBUG) { @@ -24,9 +28,11 @@ object DebugLogger { } /** - * @param tag tag to log - * @param message message to log - * @param throwable error to log + * Logs an info message. + * + * @param tag The tag to associate with the log message. + * @param message The message to log. + * @param throwable An optional [Throwable] to include in the log message. */ fun info(tag: String, message: String, throwable: Throwable? = null) { if (BuildConfig.DEBUG) { @@ -35,9 +41,11 @@ object DebugLogger { } /** - * @param tag tag to log - * @param message message to log - * @param throwable error to log + * Logs a warning message. + * + * @param tag The tag to associate with the log message. + * @param message The message to log. + * @param throwable An optional [Throwable] to include in the log message. */ fun warning(tag: String, message: String, throwable: Throwable? = null) { if (BuildConfig.DEBUG) { @@ -46,9 +54,11 @@ object DebugLogger { } /** - * @param tag tag to log - * @param message message to log - * @param throwable error to log + * Logs an error message. + * + * @param tag The tag to associate with the log message. + * @param message The message to log. + * @param throwable An optional [Throwable] to include in the log message. */ fun error(tag: String, message: String, throwable: Throwable? = null) { if (BuildConfig.DEBUG) { diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/utils/Heartbeat.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/utils/Heartbeat.kt index a8bca4c15..f3f917c56 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/utils/Heartbeat.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/utils/Heartbeat.kt @@ -14,12 +14,12 @@ import kotlin.coroutines.CoroutineContext import kotlin.time.Duration /** - * Utility class to trigger a [task] at a regular [intervals][period]. + * A utility class that repeatedly executes the given [task] at a specified [period]. * - * @param startDelay The initial delay before the first execution of [task]. - * @param period The period between two executions of [task]. - * @param coroutineContext The coroutine context in which [Heartbeat] is run. - * @param task The task to execute at regular [intervals][period]. + * @param startDelay The initial delay before the first execution of the [task]. + * @param period The time interval between consecutive executions of the [task]. + * @param coroutineContext The coroutine context in which the heartbeat will run. + * @param task The function to be executed periodically. */ class Heartbeat( private val startDelay: Duration = Duration.ZERO, @@ -32,11 +32,13 @@ class Heartbeat( private var job: Job? = null /** - * Start the execution of this heartbeat. Does nothing if it is already running and [restart] is `false`. + * Starts the execution of this heartbeat. * - * @param restart `true` to restart the heartbeat if it is already running, `false` otherwise. + * If the heartbeat is already running, this function behaves based on the [restart] parameter: + * - If [restart] is `true`, the current heartbeat execution is stopped and a new one is started. + * - If [restart] is `false`, the function does nothing and the current heartbeat continues running. * - * @see stop + * @param restart Indicates whether to restart the heartbeat if it's already running. */ fun start(restart: Boolean = true) { if (job?.isActive == true && !restart) { @@ -55,7 +57,7 @@ class Heartbeat( } /** - * Stop the execution of this heartbeat. + * Stops the execution of this heartbeat. */ fun stop() { job?.cancel() diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/utils/PendingIntentUtils.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/utils/PendingIntentUtils.kt index c1a02e8ad..2dde5c2a5 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/utils/PendingIntentUtils.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/utils/PendingIntentUtils.kt @@ -4,16 +4,20 @@ */ package ch.srgssr.pillarbox.player.utils +import android.app.Activity import android.app.PendingIntent import android.content.Context import android.os.Build /** - * PendingIntent utils + * Utility class providing helper functions for working with [PendingIntent]s. */ object PendingIntentUtils { /** - * Try to get application launcher intent + * Retrieves a [PendingIntent] that launches the default [Activity] of the application. + * + * @param context The [Context]. + * @return A [PendingIntent] that launches the default [Activity], or `null` if it could not be created. */ @JvmStatic fun getDefaultPendingIntent(context: Context): PendingIntent? { @@ -23,9 +27,11 @@ object PendingIntentUtils { } /** - * From Android 23, PendingIntent needs to add PendingIntent.FLAG_IMMUTABLE - * @param flags add [PendingIntent.FLAG_IMMUTABLE] for android 23+ - * @return [flags] with IMMUTABLE flag. + * Adds the [PendingIntent.FLAG_IMMUTABLE] flag to the provided [flags] if the device is running Android 6.0 (Marshmallow) or higher. + * + * @param flags The initial flags of the [PendingIntent]. + * @return The provided [flags] with the [PendingIntent.FLAG_IMMUTABLE] flag added if the device is running Android 6.0 or higher, otherwise the + * original [flags] unchanged. */ @JvmStatic fun appendImmutableFlagIfNeeded(flags: Int): Int { diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/utils/PillarboxEventLogger.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/utils/PillarboxEventLogger.kt index 040a5f561..63326358b 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/utils/PillarboxEventLogger.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/utils/PillarboxEventLogger.kt @@ -15,10 +15,9 @@ import ch.srgssr.pillarbox.player.asset.timeRange.Credit import kotlin.time.Duration.Companion.milliseconds /** - * Pillarbox event logger + * A logger for Pillarbox analytics events. * - * @param tag The tag to use for logging - * @constructor Create empty Pillarbox event logger + * @param tag The tag to use for logging. */ class PillarboxEventLogger(private val tag: String = "EventLogger") : EventLogger(tag), PillarboxAnalyticsListener { private val startTimeMs: Long = SystemClock.elapsedRealtime() diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/utils/StringUtil.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/utils/StringUtil.kt index e1b9a650a..33ed2d665 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/utils/StringUtil.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/utils/StringUtil.kt @@ -11,13 +11,16 @@ import androidx.media3.common.Player.State import androidx.media3.common.Player.TimelineChangeReason /** - * String util toString some Player constant enum + * A utility class that provides string representations for various [Player] constants and enums. */ object StringUtil { private const val UNKNOWN = "UNKNOWN" /** - * Media item transition reason string + * Converts a media item transition reason integer value to its corresponding string representation. + * + * @param value The [MediaItemTransitionReason]. + * @return A string representation of the media item transition reason. */ fun mediaItemTransitionReasonString(value: @MediaItemTransitionReason Int): String { return when (value) { @@ -30,7 +33,10 @@ object StringUtil { } /** - * Player state string + * Converts a player state integer value to its corresponding string representation. + * + * @param value The [State]. + * @return A string representation of the player state. */ fun playerStateString(value: @State Int): String { return when (value) { @@ -43,7 +49,10 @@ object StringUtil { } /** - * Timeline change reason string + * Converts a timeline change reason integer value to its corresponding string representation. + * + * @param value The [TimelineChangeReason]. + * @return A string representation of the timeline change reason. */ fun timelineChangeReasonString(value: @TimelineChangeReason Int): String { return when (value) { @@ -54,7 +63,10 @@ object StringUtil { } /** - * Discontinuity reason string + * Converts a discontinuity reason integer value to its corresponding string representation. + * + * @param value The [DiscontinuityReason]. + * @return A string representation of the discontinuity reason. */ fun discontinuityReasonString(value: @DiscontinuityReason Int): String { return when (value) { From 694228de86020732dad82eeb7619924311494a66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Muller?= Date: Wed, 27 Nov 2024 10:42:03 +0100 Subject: [PATCH 15/17] Add missing case to `StringUtil.discontinuityReasonString()` --- .../src/main/java/ch/srgssr/pillarbox/player/utils/StringUtil.kt | 1 + .../test/java/ch/srgssr/pillarbox/player/utils/StringUtilTest.kt | 1 + 2 files changed, 2 insertions(+) diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/utils/StringUtil.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/utils/StringUtil.kt index 33ed2d665..1020abe73 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/utils/StringUtil.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/utils/StringUtil.kt @@ -76,6 +76,7 @@ object StringUtil { Player.DISCONTINUITY_REASON_SKIP -> "DISCONTINUITY_REASON_SKIP" Player.DISCONTINUITY_REASON_SEEK_ADJUSTMENT -> "DISCONTINUITY_REASON_SEEK_ADJUSTMENT" Player.DISCONTINUITY_REASON_AUTO_TRANSITION -> "DISCONTINUITY_REASON_AUTO_TRANSITION" + Player.DISCONTINUITY_REASON_SILENCE_SKIP -> "DISCONTINUITY_REASON_SILENCE_SKIP" else -> UNKNOWN } } diff --git a/pillarbox-player/src/test/java/ch/srgssr/pillarbox/player/utils/StringUtilTest.kt b/pillarbox-player/src/test/java/ch/srgssr/pillarbox/player/utils/StringUtilTest.kt index 2363438fd..cf9ad3a57 100644 --- a/pillarbox-player/src/test/java/ch/srgssr/pillarbox/player/utils/StringUtilTest.kt +++ b/pillarbox-player/src/test/java/ch/srgssr/pillarbox/player/utils/StringUtilTest.kt @@ -44,6 +44,7 @@ class StringUtilTest { assertEquals("DISCONTINUITY_REASON_REMOVE", StringUtil.discontinuityReasonString(Player.DISCONTINUITY_REASON_REMOVE)) assertEquals("DISCONTINUITY_REASON_SEEK", StringUtil.discontinuityReasonString(Player.DISCONTINUITY_REASON_SEEK)) assertEquals("DISCONTINUITY_REASON_SEEK_ADJUSTMENT", StringUtil.discontinuityReasonString(Player.DISCONTINUITY_REASON_SEEK_ADJUSTMENT)) + assertEquals("DISCONTINUITY_REASON_SILENCE_SKIP", StringUtil.discontinuityReasonString(Player.DISCONTINUITY_REASON_SILENCE_SKIP)) assertEquals("DISCONTINUITY_REASON_SKIP", StringUtil.discontinuityReasonString(Player.DISCONTINUITY_REASON_SKIP)) assertEquals("UNKNOWN", StringUtil.discontinuityReasonString(42)) } From e4bee3fdfab9e0767940fc1f0d2cea3357e4d676 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Muller?= Date: Wed, 27 Nov 2024 14:12:02 +0100 Subject: [PATCH 16/17] Review KDoc in `ch.srgssr.pillarbox.player` --- .../player/PillarboxBandwidthMeter.kt | 5 +- .../pillarbox/player/PillarboxBuilder.kt | 96 +++++++-------- .../pillarbox/player/PillarboxExoPlayer.kt | 8 ++ .../pillarbox/player/PillarboxLoadControl.kt | 11 +- .../pillarbox/player/PillarboxPlayer.kt | 58 +++++----- .../player/PillarboxPreloadManager.kt | 36 +++--- .../player/PillarboxRenderersFactory.kt | 5 +- .../player/PillarboxTrackSelector.kt | 5 +- .../pillarbox/player/PlayerCallbackFlow.kt | 109 +++++++++++++----- .../pillarbox/player/PreloadConfiguration.kt | 6 +- 10 files changed, 207 insertions(+), 132 deletions(-) diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PillarboxBandwidthMeter.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PillarboxBandwidthMeter.kt index c4231fd82..3de4efa6f 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PillarboxBandwidthMeter.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PillarboxBandwidthMeter.kt @@ -9,9 +9,10 @@ import androidx.media3.exoplayer.upstream.BandwidthMeter import androidx.media3.exoplayer.upstream.DefaultBandwidthMeter /** - * Preconfigured [BandwidthMeter] for Pillarbox. + * Provides a pre-configured instance of [BandwidthMeter] suitable for use within Pillarbox. * - * @param context The [Context] needed to create the [BandwidthMeter]. + * @param context The [Context] required for initializing the [BandwidthMeter]. + * @return A [BandwidthMeter] ready for use within Pillarbox. */ @Suppress("FunctionName") fun PillarboxBandwidthMeter(context: Context): BandwidthMeter { diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PillarboxBuilder.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PillarboxBuilder.kt index a8cdeb0b3..8dbb4f843 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PillarboxBuilder.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PillarboxBuilder.kt @@ -36,13 +36,20 @@ import kotlin.time.Duration.Companion.ZERO import kotlin.time.Duration.Companion.milliseconds /** - * Marker for Pillarbox's DSL. + * Marks a class or function as part of the Pillarbox DSL (Domain Specific Language). + * + * This annotation serves as a marker for the Kotlin compiler, enabling DSL-specific features like type-safe builders and improved code completion. + * Applying this annotation to a class or function indicates that it's intended to be used within the context of the Pillarbox DSL. + * + * This annotation is primarily intended for internal use within the Pillarbox library. */ @DslMarker annotation class PillarboxDsl /** - * Builder to create a new instance of [PillarboxExoPlayer]. + * A builder class for creating instances of [PillarboxExoPlayer]. + * + * This builder provides a fluent API for configuring various aspects of the player, such as asset loaders, coroutine context, seek increments, ... */ @PillarboxDsl @Suppress("TooManyFunctions") @@ -61,16 +68,16 @@ abstract class PillarboxBuilder { private var preloadConfiguration = ExoPlayer.PreloadConfiguration.DEFAULT /** - * Add an [AssetLoader] to the [PillarboxExoPlayer]. + * Registers a custom [AssetLoader] with the [PillarboxExoPlayer]. * - * @param assetLoader The [assetLoader] to add. + * @param assetLoader The [AssetLoader] to add. */ fun addAssetLoader(assetLoader: AssetLoader) { assetLoaders.add(assetLoader) } /** - * Add an [AssetLoader] to the [PillarboxExoPlayer]. + * Registers a custom [AssetLoader] with the [PillarboxExoPlayer]. * * @receiver The [AssetLoader] to add. */ @@ -79,9 +86,11 @@ abstract class PillarboxBuilder { } /** - * Set the internal [Clock] used by the player. + * Sets the internal [Clock] used by the player. * - * @param clock The internal clock used by the player. + * **Note:** this function is intended for internal use and should not be called by applications. + * + * @param clock The [Clock] instance to be used by the player. */ @VisibleForTesting @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) @@ -90,41 +99,41 @@ abstract class PillarboxBuilder { } /** - * Set the coroutine context used by the player. + * Sets the [CoroutineContext] used by the player. * - * @param coroutineContext The coroutine context used by the player. + * @param coroutineContext The [CoroutineContext] to be used by the player. */ fun coroutineContext(coroutineContext: CoroutineContext) { this.coroutineContext = coroutineContext } /** - * Set the load control used by the player. + * Sets the [LoadControl] used by the player. * - * @param loadControl The load control used by the player. + * @param loadControl The [LoadControl] to be used by the player. */ fun loadControl(loadControl: LoadControl) { this.loadControl = loadControl } /** - * Set the [Player.getMaxSeekToPreviousPosition] value. + * Sets the maximum duration the player can seek backward when using [Player.seekToPrevious]. * - * @param maxSeekToPreviousPosition The [Player.getMaxSeekToPreviousPosition] value. + * @param maxSeekToPreviousPosition The maximum duration to seek backward. */ fun maxSeekToPreviousPosition(maxSeekToPreviousPosition: Duration) { this.maxSeekToPreviousPosition = maxSeekToPreviousPosition } /** - * Disable the monitoring for this player + * Disables the monitoring for this player. */ fun disableMonitoring() { monitoring = NoOp() } /** - * Make the monitoring logs all events to Logcat, using the default config. + * Logs all monitoring events to Logcat. * * @param type [Logcat]. */ @@ -135,11 +144,11 @@ abstract class PillarboxBuilder { } /** - * Make the monitoring sends all events to a remote server. + * Configures the monitoring to send all events to a remote server. * - * @param endpointUrl The endpoint receiving monitoring messages. - * @param httpClient The [HttpClient] to use to send the events. - * @param coroutineScope The scope used to send the monitoring message. + * @param endpointUrl The URL of the endpoint responsible for receiving monitoring messages. + * @param httpClient The [HttpClient] instance used for transmitting events to the endpoint. + * @param coroutineScope The [CoroutineScope] which manages the coroutine responsible for sending monitoring messages. */ fun monitoring( endpointUrl: String, @@ -152,12 +161,12 @@ abstract class PillarboxBuilder { } /** - * Configure the monitoring for this player. + * Configures monitoring for this player. * - * @param Config The type of the config to create. - * @param Factory The type of the [MonitoringMessageHandlerFactory]. - * @param type The type of [MonitoringMessageHandler] to use. - * @param createConfig The configuration builder to create the [MonitoringMessageHandler]. + * @param Config The type of the configuration object used to setup the monitoring handler. + * @param Factory The type of the [MonitoringMessageHandlerFactory] used to create the monitoring handler. + * @param type The type of [MonitoringMessageHandler] to create. + * @param createConfig A lambda that returns a configuration of type [Config]. */ fun > monitoring( type: MonitoringMessageHandlerType, @@ -167,48 +176,41 @@ abstract class PillarboxBuilder { } /** - * Set the [Looper] to use for playback. + * Sets the [Looper] used by the player. * - * @param playbackLooper The [Looper] used for playback. + * @param playbackLooper The [Looper] to be used by the player. */ fun playbackLooper(playbackLooper: Looper) { this.playbackLooper = playbackLooper } /** - * Set the seek back increment duration. + * Sets the duration by which the player seeks backward when performing a "seek backward" operation. * - * @param seekBackwardIncrement The seek back increment duration. + * @param seekBackwardIncrement The duration to seek backward by. */ fun seekBackwardIncrement(seekBackwardIncrement: Duration) { this.seekBackwardIncrement = seekBackwardIncrement } /** - * Set the seek forward increment duration. + * Sets the duration by which the player seeks forward when performing a "seek forward" action. * - * @param seekForwardIncrement The seek forward increment duration. + * @param seekForwardIncrement The duration to seek forward by. */ fun seekForwardIncrement(seekForwardIncrement: Duration) { this.seekForwardIncrement = seekForwardIncrement } /** - * Set the [ExoPlayer.PreloadConfiguration] used by the player. + * Sets the [ExoPlayer.PreloadConfiguration] used by the player. * - * @param preloadConfiguration The [ExoPlayer.PreloadConfiguration]. + * @param preloadConfiguration The [ExoPlayer.PreloadConfiguration] to be used by the player. */ fun preloadConfiguration(preloadConfiguration: ExoPlayer.PreloadConfiguration) { this.preloadConfiguration = preloadConfiguration } - /** - * Create a new instance of [PillarboxExoPlayer]. - * - * @param context The [Context]. - * - * @return A new instance of [PillarboxExoPlayer]. - */ internal fun create(context: Context): PillarboxExoPlayer { return PillarboxExoPlayer( context = context, @@ -221,7 +223,9 @@ abstract class PillarboxBuilder { } /** - * Create a new instance of [ExoPlayer.Builder], used internally by [PillarboxExoPlayer]. + * Creates the instance of [ExoPlayer.Builder], that will be used internally by [PillarboxExoPlayer]. + * + * Subclasses can override this method to customize the [ExoPlayer.Builder] further, but they **MUST** ensure to call the super implementation. * * @param context The [Context]. * @@ -256,19 +260,21 @@ abstract class PillarboxBuilder { } /** - * Factory used to create instances of [PillarboxBuilder]. + * Defines a factory for creating instances of [PillarboxBuilder]. * - * @param Builder The type of [PillarboxBuilder] to create. + * @param Builder The type of [PillarboxBuilder] that this factory creates. */ interface PlayerConfig { /** - * Create a new instance of [Builder]. + * Creates a new instance of the [Builder] class. + * + * @return A new instance of the [Builder]. */ fun create(): Builder } /** - * Default implementation used to create simple [PillarboxExoPlayer]. + * Default configuration for creating a [PillarboxExoPlayer], which closely matches an [ExoPlayer]. */ object Default : PlayerConfig { override fun create(): Builder { @@ -276,7 +282,7 @@ object Default : PlayerConfig { } /** - * Default implementation used to create simple [PillarboxExoPlayer]. + * A builder class for creating and configuring a [PillarboxExoPlayer]. */ class Builder : PillarboxBuilder() { init { diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PillarboxExoPlayer.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PillarboxExoPlayer.kt index 94bf07516..1d07a8b23 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PillarboxExoPlayer.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PillarboxExoPlayer.kt @@ -38,6 +38,14 @@ import kotlin.coroutines.CoroutineContext /** * Create a new instance of [PillarboxExoPlayer]. * + * **Usage** + * ```kotlin + * val player = PillarboxExoPlayer(context, Default) { + * addAssetLoader(MyAssetLoader()) + * coroutineContext(Dispatchers.Main) + * } + * ``` + * * @param Builder The type of the [PillarboxBuilder]. * @param context The [Context]. * @param type The [PlayerConfig]. diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PillarboxLoadControl.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PillarboxLoadControl.kt index e58cfec1b..43aee9add 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PillarboxLoadControl.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PillarboxLoadControl.kt @@ -20,9 +20,9 @@ import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.seconds /** - * Pillarbox [LoadControl] implementation that optimize content loading. + * A [LoadControl] implementation tailored for Pillarbox, optimizing content loading. * - * @param bufferDurations Buffer durations to set [DefaultLoadControl.Builder.setBufferDurationsMs]. + * @param bufferDurations Buffer durations to customize the internal [DefaultLoadControl]'s behavior. * @param allocator The [DefaultAllocator] to use in the internal [DefaultLoadControl]. */ class PillarboxLoadControl( @@ -91,14 +91,13 @@ class PillarboxLoadControl( } /** - * Buffer durations to use for [DefaultLoadControl.Builder.setBufferDurationsMs]. + * Represents the buffer durations used by [DefaultLoadControl.Builder.setBufferDurationsMs]. * * @property minBufferDuration The minimum duration of media that the player will attempt to ensure is buffered at all times. * @property maxBufferDuration The maximum duration of media that the player will attempt to buffer. * @property bufferForPlayback The duration of media that must be buffered for playback to start or resume following a user action such as a seek. - * @property bufferForPlaybackAfterRebuffer The default duration of media that must be buffered for playback to resume after a rebuffer. - * A rebuffer is defined to be caused by buffer depletion rather than a user action. - * @constructor Create empty Buffer durations + * @property bufferForPlaybackAfterRebuffer The duration of media that must be buffered for playback to resume after a rebuffer. A rebuffer is + * defined to be caused by buffer depletion rather than a user action. */ data class BufferDurations( val minBufferDuration: Duration = DefaultLoadControl.DEFAULT_MIN_BUFFER_MS.milliseconds, diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PillarboxPlayer.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PillarboxPlayer.kt index aa0e91cfe..f4a9c1a9b 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PillarboxPlayer.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PillarboxPlayer.kt @@ -16,65 +16,67 @@ import ch.srgssr.pillarbox.player.asset.timeRange.Credit */ interface PillarboxPlayer : Player { /** - * Listener + * A listener for events specific to Pillarbox. */ interface Listener : Player.Listener { /** - * On smooth seeking enabled changed + * Called when the smooth seeking enabled state changes. * - * @param smoothSeekingEnabled The new value of [PillarboxPlayer.smoothSeekingEnabled] + * @param smoothSeekingEnabled Whether smooth seeking is enabled. */ fun onSmoothSeekingEnabledChanged(smoothSeekingEnabled: Boolean) {} /** - * On tracking enabled changed + * Called when the tracking state changes. * - * @param trackingEnabled The new value of [PillarboxPlayer.trackingEnabled] + * @param trackingEnabled Whether tracking is enabled. */ fun onTrackingEnabledChanged(trackingEnabled: Boolean) {} /** - * `onChapterChanged` is called when either: - * - The player position changes while playing automatically. - * - The use seeks to a new position. - * - The playlist changes. + * Called when the current chapter changes. This can occur due to several reasons: * - * @param chapter `null` when the current position is not in a chapter. + * - **Automatic playback:** the player's position progresses naturally during playback and enters a new chapter. + * - **Seeking:** the user manually seeks to a new position within the content, landing within a different chapter. + * - **Playlist change:** the current playlist is changed, potentially resulting in a different set of chapters and a new active chapter. + * + * @param chapter The currently active [Chapter]. This will be `null` if the current playback position is not within any defined chapter. */ fun onChapterChanged(chapter: Chapter?) {} /** - * On blocked time range reached + * Called when the player reaches a blocked time range. * - * @param blockedTimeRange The [BlockedTimeRange] reached by the player. + * @param blockedTimeRange The [BlockedTimeRange] representing the time range that the player has reached. */ fun onBlockedTimeRangeReached(blockedTimeRange: BlockedTimeRange) {} /** - * `onCreditChanged` is called when either: - * - The player position changes while playing automatically. - * - The use seeks to a new position. - * - The playlist changes. + * Called when the current credit changes. This can occur due to several reasons: + * + * - **Automatic playback:** the player's position progresses naturally during playback and enters a new chapter. + * - **Seeking:** the user manually seeks to a new position within the content, landing within a different chapter. + * - **Playlist change:** the current playlist is changed, potentially resulting in a different set of chapters and a new active chapter. * - * @param credit `null` when the current position is not in a Credit. + * @param credit The currently active [Credit]. This will be `null` if the current playback position is not within any defined credit. */ fun onCreditChanged(credit: Credit?) {} } /** - * Smooth seeking enabled + * Controls whether smooth seeking behavior is enabled. * - * When [smoothSeekingEnabled] is true, next seek events is send only after the current is done. + * When this property is `true`, subsequent seek events are sent only after the current seek operation is completed. * - * To have the best result it is important to - * 1) Pause the player while seeking. - * 2) Set the [ExoPlayer.setSeekParameters] to [SeekParameters.CLOSEST_SYNC]. + * For optimal results, it is important to: + * 1. Pause the player during seek operations. + * 2. Set the player's seek parameters to [SeekParameters.CLOSEST_SYNC] using [ExoPlayer.setSeekParameters]. */ var smoothSeekingEnabled: Boolean /** - * Enable or disable MediaItem tracking + * Controls whether media item tracking is enabled. */ var trackingEnabled: Boolean @@ -82,27 +84,27 @@ interface PillarboxPlayer : Player { companion object { /** - * Event Blocked Time Range Reached. + * Event indicating that a blocked time range has been reached. */ const val EVENT_BLOCKED_TIME_RANGE_REACHED = 100 /** - * The current [Chapter] has changed. + * Event indicating that the current [Chapter] has changed. */ const val EVENT_CHAPTER_CHANGED = 101 /** - * The current [Credit] Changed. + * Event indicating that the current [Credit] has changed. */ const val EVENT_CREDIT_CHANGED = 102 /** - * [trackingEnabled] has changed. + * Event indicating that the media item [tracking state][trackingEnabled] has changed. */ const val EVENT_TRACKING_ENABLED_CHANGED = 103 /** - * [smoothSeekingEnabled] has changed. + * Event indicating that the [smooth seeking state][smoothSeekingEnabled] has changed. */ const val EVENT_SMOOTH_SEEKING_ENABLED_CHANGED = 104 } diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PillarboxPreloadManager.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PillarboxPreloadManager.kt index fae7a0b9f..fe5b83e34 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PillarboxPreloadManager.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PillarboxPreloadManager.kt @@ -25,9 +25,9 @@ import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.seconds /** - * Helper class for the Media3's [DefaultPreloadManager]. + * Helper class for Media3's [DefaultPreloadManager] that simplifies preloading of media items for [PillarboxExoPlayer]. * - * @param context The current [Context]. + * @param context The [Context]. * @param targetPreloadStatusControl The [TargetPreloadStatusControl] to decide when to preload an item and for how long. * @param mediaSourceFactory The [PillarboxMediaSourceFactory] to create each [MediaSource]. * @param trackSelector The [TrackSelector] for this preload manager. @@ -62,15 +62,16 @@ class PillarboxPreloadManager( } /** - * Get the count of [MediaSource] currently managed by this preload manager. + * Gets the count of the [MediaSource]s currently being managed by the preload manager. * + * @return The count of the [MediaSource]s. * @see DefaultPreloadManager.getSourceCount */ val sourceCount: Int get() = preloadManager.sourceCount /** - * Playback looper to use with PillarboxExoPlayer. + * The [Looper] associated with the [Thread] on which playback operations are performed by the [PillarboxExoPlayer]. */ val playbackLooper: Looper @@ -89,7 +90,7 @@ class PillarboxPreloadManager( } /** - * Add a [MediaItem] with its [rankingData] to the preload manager. + * Adds a [MediaItem] with its [rankingData] to the preload manager. * * @param mediaItem The [MediaItem] to add. * @param rankingData The ranking data that is associated with the [mediaItem]. @@ -100,7 +101,7 @@ class PillarboxPreloadManager( } /** - * Add a [MediaSource] with its [rankingData] to the preload manager. + * Adds a [MediaSource] with its [rankingData] to the preload manager. * * @param mediaSource The [MediaSource] to add. * @param rankingData The ranking data that is associated with the [mediaSource]. @@ -114,7 +115,7 @@ class PillarboxPreloadManager( * Returns the [MediaSource] for the given [MediaItem]. * * @param mediaItem The [MediaItem]. - * @return The source for the give [mediaItem] if it is managed by the preload manager, `null` otherwise. + * @return The source for the given [mediaItem] if it is managed by the preload manager, `null` otherwise. * @see DefaultPreloadManager.getMediaSource */ fun getMediaSource(mediaItem: MediaItem): MediaSource? { @@ -122,7 +123,7 @@ class PillarboxPreloadManager( } /** - * Invalidate the current preload manager. + * Invalidates the current preload progress, and triggers a new preload progress based on the new priorities of the managed [MediaSource]s. * * @see DefaultPreloadManager.invalidate */ @@ -131,7 +132,7 @@ class PillarboxPreloadManager( } /** - * Release the preload manager. + * Releases the preload manager. * The preload manager must not be used after calling this method. * * @see DefaultPreloadManager.release @@ -142,7 +143,7 @@ class PillarboxPreloadManager( } /** - * Remove a [MediaItem] from the preload manager. + * Removes a [MediaItem] from the preload manager. * * @param mediaItem The [MediaItem] to remove. * @return `true` if the preload manager is holding a [MediaSource] of the given [MediaItem] and it has been removed, `false` otherwise. @@ -153,7 +154,7 @@ class PillarboxPreloadManager( } /** - * Remove a [MediaSource] from the preload manager. + * Removes a [MediaSource] from the preload manager. * * @param mediaSource The [MediaSource] to remove. * @return `true` if the preload manager is holding the given [MediaSource] and it has been removed, `false` otherwise. @@ -164,7 +165,7 @@ class PillarboxPreloadManager( } /** - * Reset the preload manager. All sources that the preload manager is holding will be released. + * Resets the preload manager. All sources that the preload manager is holding will be released. * * @see DefaultPreloadManager.reset */ @@ -173,8 +174,15 @@ class PillarboxPreloadManager( } /** - * Default implementation of [TargetPreloadStatusControl] that will preload the first second of the `n ± 1` item, and the first half-second of - * the `n ± 2,3` item, where `n` is the index of the current item. + * Default implementation of [TargetPreloadStatusControl] that manages the preload status of items based on their proximity to the currently + * playing item. + * + * This implementation uses a simple distance-based strategy: + * - The item immediately before or after the current item (offset of 1) is preloaded to 1 second. + * - The items two or three positions away from the current item (offset of 2 or 3) are preloaded to 0.5 seconds. + * - All other items are not preloaded. + * + * This strategy aims to preload content that is likely to be played soon, reducing buffering and improving playback smoothness. */ @Suppress("MagicNumber") inner class DefaultTargetPreloadStatusControl : TargetPreloadStatusControl { diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PillarboxRenderersFactory.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PillarboxRenderersFactory.kt index ce58af8e2..e126ba61b 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PillarboxRenderersFactory.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PillarboxRenderersFactory.kt @@ -9,9 +9,10 @@ import androidx.media3.exoplayer.DefaultRenderersFactory import androidx.media3.exoplayer.RenderersFactory /** - * Preconfigured [RenderersFactory] for Pillarbox. + * Provides a pre-configured instance of [RenderersFactory] suitable for use within Pillarbox. * - * @param context The [Context] needed to create the [RenderersFactory]. + * @param context The [Context] required for initializing the [RenderersFactory]. + * @return A [RenderersFactory] ready for use within Pillarbox. */ @Suppress("FunctionName") fun PillarboxRenderersFactory(context: Context): RenderersFactory { diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PillarboxTrackSelector.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PillarboxTrackSelector.kt index c5a910278..206caab92 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PillarboxTrackSelector.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PillarboxTrackSelector.kt @@ -11,9 +11,10 @@ import androidx.media3.exoplayer.trackselection.TrackSelector import ch.srgssr.pillarbox.player.extension.setPreferredAudioRoleFlagsToAccessibilityManagerSettings /** - * Preconfigured [TrackSelector] for Pillarbox. + * Provides a pre-configured instance of [TrackSelector] suitable for use within Pillarbox. * - * @param context The [Context] needed to create the [TrackSelector]. + * @param context The [Context] required for initializing the [TrackSelector]. + * @return A [TrackSelector] ready for use within Pillarbox. */ @Suppress("FunctionName") fun PillarboxTrackSelector(context: Context): TrackSelector { diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PlayerCallbackFlow.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PlayerCallbackFlow.kt index 61e220619..75671a46e 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PlayerCallbackFlow.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PlayerCallbackFlow.kt @@ -44,7 +44,9 @@ import kotlin.time.Duration import kotlin.time.Duration.Companion.seconds /** - * Playback state [Player.getPlaybackState] as flow. + * Collects the [playback state][Player.getPlaybackState] as a [Flow]. + * + * @return A [Flow] emitting the playback state. */ fun Player.playbackStateAsFlow(): Flow = callbackFlow { val listener = object : Listener { @@ -57,7 +59,9 @@ fun Player.playbackStateAsFlow(): Flow = callbackFlow { } /** - * PlayerError [Player.getPlayerError] as Flow. + * Collects the [playback error][Player.getPlayerError] as a [Flow]. + * + * @return A [Flow] emitting the playback error. */ fun Player.playerErrorAsFlow(): Flow = callbackFlow { val listener = object : Listener { @@ -70,7 +74,9 @@ fun Player.playerErrorAsFlow(): Flow = callbackFlow { } /** - * Is playing [Player.isPlaying] as Flow. + * Collects whether the player [is playing][Player.isPlaying] as a [Flow]. + * + * @return A [Flow] emitting whether the player is playing. */ fun Player.isPlayingAsFlow(): Flow = callbackFlow { val listener = object : Listener { @@ -83,7 +89,9 @@ fun Player.isPlayingAsFlow(): Flow = callbackFlow { } /** - * Duration [Player.getDuration] as Flow. + * Collects the [duration][Player.getDuration] as a [Flow]. + * + * @return A [Flow] emitting the duration. */ fun Player.durationAsFlow(): Flow = callbackFlow { val listener = object : Listener { @@ -102,7 +110,9 @@ fun Player.durationAsFlow(): Flow = callbackFlow { } /** - * Playback speed [Player.getPlaybackSpeed] as Flow. + * Collects the [playback speed][Player.getPlaybackSpeed] as a [Flow]. + * + * @return A [Flow] emitting the playback speed. */ fun Player.getPlaybackSpeedAsFlow(): Flow = callbackFlow { val listener = object : Listener { @@ -115,7 +125,9 @@ fun Player.getPlaybackSpeedAsFlow(): Flow = callbackFlow { } /** - * Available commands [Player.getAvailableCommands] as Flow. + * Collects the [available commands][Player.getAvailableCommands] as a [Flow]. + * + * @return A [Flow] emitting the available commands. */ fun Player.availableCommandsAsFlow(): Flow = callbackFlow { val listener = object : Listener { @@ -128,7 +140,9 @@ fun Player.availableCommandsAsFlow(): Flow = callbackFlow { } /** - * Shuffle mode enabled [Player.getShuffleModeEnabled] as Flow. + * Collects whether the [shuffle mode is enabled][Player.getShuffleModeEnabled] as a [Flow]. + * + * @return A [Flow] emitting whether the shuffle mode is enabled. */ fun Player.shuffleModeEnabledAsFlow(): Flow = callbackFlow { val listener = object : Listener { @@ -141,7 +155,9 @@ fun Player.shuffleModeEnabledAsFlow(): Flow = callbackFlow { } /** - * Media item count [Player.getMediaItemCount] as Flow. + * Collects the [media item count][Player.getMediaItemCount] as a [Flow]. + * + * @return A [Flow] emitting the media item count. */ fun Player.mediaItemCountAsFlow(): Flow = callbackFlow { val listener = object : Listener { @@ -160,8 +176,10 @@ fun Player.mediaItemCountAsFlow(): Flow = callbackFlow { } /** - * Ticker emits event every [interval] when [Player.isPlaying] is true. - * Emit a value once at least once. + * Emits an event every [interval] while the [Player] is playing. + * + * @param interval The time interval between emissions. + * @return A [Flow] that emits at the specified interval while the player is playing. */ @OptIn(ExperimentalCoroutinesApi::class) fun Player.tickerWhilePlayingAsFlow( @@ -174,8 +192,10 @@ fun Player.tickerWhilePlayingAsFlow( } /** - * Current position of the player updates every [updateInterval] when it is playing. - * Send current position once if not playing. + * Collects the [current position][Player.getCurrentPosition] of the player as a [Flow]. + * + * @param updateInterval The time interval between emissions, if the player is playing. + * @return A [Flow] emitting the current position of the player, in milliseconds. */ fun Player.currentPositionAsFlow(updateInterval: Duration = DefaultUpdateInterval): Flow = merge( @@ -200,9 +220,10 @@ private fun Player.positionChangedFlow(): Flow = callbackFlow { }.distinctUntilChanged() /** - * Current buffered percentage as flow [Player.getBufferedPercentage] + * Collects the [buffered percentage][Player.getBufferedPercentage] as a [Flow]. * - * @param updateInterval The update interval + * @param updateInterval The time interval between emissions, if the player is playing. + * @return A [Flow] emitting the buffered percentage. */ @Suppress("MagicNumber") fun Player.currentBufferedPercentageAsFlow( @@ -212,7 +233,9 @@ fun Player.currentBufferedPercentageAsFlow( } /** - * Current media metadata as flow [Player.getCurrentMediaItem] + * Collects the [current media item][Player.getCurrentMediaItem] as a [Flow]. + * + * @return A [Flow] emitting the current media item. */ fun Player.currentMediaItemAsFlow(): Flow = callbackFlow { val listener = object : Listener { @@ -231,9 +254,10 @@ fun Player.currentMediaItemAsFlow(): Flow = callbackFlow { } /** - * Current media metadata as flow [Player.getMediaMetadata] + * Collects the [media metadata][Player.getMediaMetadata] as a [Flow]. * - * @param withPlaylistMediaMetadata try to listen [Player.Listener.onPlaylistMetadataChanged] too. + * @param withPlaylistMediaMetadata Whether to listen to [Player.Listener.onPlaylistMetadataChanged] too. + * @return A [Flow] emitting the media metadata. */ fun Player.currentMediaMetadataAsFlow(withPlaylistMediaMetadata: Boolean = false): Flow = callbackFlow { val listener = object : Listener { @@ -252,7 +276,9 @@ fun Player.currentMediaMetadataAsFlow(withPlaylistMediaMetadata: Boolean = false } /** - * Get current media item index as flow [Player.getCurrentMediaItemIndex] + * Collects the [current media item index][Player.getCurrentMediaItemIndex] as a [Flow]. + * + * @return A [Flow] emitting the current media item index. */ fun Player.getCurrentMediaItemIndexAsFlow(): Flow = callbackFlow { val listener = object : Listener { @@ -271,7 +297,9 @@ fun Player.getCurrentMediaItemIndexAsFlow(): Flow = callbackFlow { } /** - * Get current media items as flow [Player.getCurrentMediaItems] + * Collects the [current media items][Player.getCurrentMediaItems] as a [Flow]. + * + * @return A [Flow] emitting the current media items. */ fun Player.getCurrentMediaItemsAsFlow(): Flow> = callbackFlow { val listener = object : Listener { @@ -284,7 +312,9 @@ fun Player.getCurrentMediaItemsAsFlow(): Flow> = callbackFlow { } /** - * Get video size as flow [Player.getVideoSize] + * Collects the [video size][Player.getVideoSize] as a [Flow]. + * + * @return A [Flow] emitting the video size. */ fun Player.videoSizeAsFlow(): Flow = callbackFlow { val listener = object : Listener { @@ -297,9 +327,10 @@ fun Player.videoSizeAsFlow(): Flow = callbackFlow { } /** - * Get aspect ratio of the current video as [Flow]. + * Collects the aspect ratio of the current video as a [Flow]. * - * @param defaultAspectRatio The aspect ratio when the video size is unknown, or for audio content. + * @param defaultAspectRatio The default aspect ration when the video size is unknown, or the content is not a video. + * @return A [Flow] emitting the aspect ratio. */ fun Player.getAspectRatioAsFlow(defaultAspectRatio: Float): Flow { return combine( @@ -313,7 +344,9 @@ fun Player.getAspectRatioAsFlow(defaultAspectRatio: Float): Flow { } /** - * Get track selection parameters as flow [Player.getTrackSelectionParameters] + * Collects the [track selection parameters][Player.getTrackSelectionParameters] as a [Flow]. + * + * @return A [Flow] emitting the track selection parameters. */ fun Player.getTrackSelectionParametersAsFlow(): Flow = callbackFlow { val listener = object : Listener { @@ -327,7 +360,9 @@ fun Player.getTrackSelectionParametersAsFlow(): Flow = } /** - * Get current tracks as flow [Player.getCurrentTracks] + * Collects the [current tracks][Player.getCurrentTracks] as a [Flow]. + * + * @return A [Flow] emitting the current tracks. */ fun Player.getCurrentTracksAsFlow(): Flow = callbackFlow { val listener = object : Listener { @@ -340,7 +375,9 @@ fun Player.getCurrentTracksAsFlow(): Flow = callbackFlow { } /** - * Play when ready as flow [Player.getPlayWhenReady] + * Collects the [play when ready state][Player.getPlayWhenReady] as a [Flow]. + * + * @return A [Flow] emitting the play when ready state. */ fun Player.playWhenReadyAsFlow(): Flow = callbackFlow { val listener = object : Listener { @@ -353,7 +390,9 @@ fun Player.playWhenReadyAsFlow(): Flow = callbackFlow { } /** - * @return `true` if current media item is a live stream. + * Collects whether the current media item [is a live stream][Player.isCurrentMediaItemLive] as a [Flow]. + * + * @return A [Flow] emitting whether the current media item is a live stream. */ fun Player.isCurrentMediaItemLiveAsFlow(): Flow = callbackFlow { val listener = object : Listener { @@ -366,7 +405,9 @@ fun Player.isCurrentMediaItemLiveAsFlow(): Flow = callbackFlow { }.distinctUntilChanged() /** - * @return The current default position as flow. + * Collects the timeline's default position, in milliseconds, as a [Flow]. + * + * @return A [Flow] emitting the timeline's default position, in milliseconds. * @see Timeline.Window.getDefaultPositionMs */ fun Player.getCurrentDefaultPositionAsFlow(): Flow = callbackFlow { @@ -395,7 +436,9 @@ fun Player.getCurrentDefaultPositionAsFlow(): Flow = callbackFlow { }.distinctUntilChanged() /** - * @return Get the current chapter as flow, when the current chapter changes. + * Collects the [current chapter][Player.getChapterAtPosition] as a [Flow]. + * + * @return A [Flow] emitting the current chapter. */ fun Player.getCurrentChapterAsFlow(): Flow = callbackFlow { val listener = object : PillarboxPlayer.Listener { @@ -408,7 +451,9 @@ fun Player.getCurrentChapterAsFlow(): Flow = callbackFlow { } /** - * @return Get the current credit as flow, when the credit changes. + * Collects the [current credit][Player.getCreditAtPosition] as a [Flow]. + * + * @return A [Flow] emitting the current credit. */ fun Player.getCurrentCreditAsFlow(): Flow = callbackFlow { val listener = object : PillarboxPlayer.Listener { @@ -421,7 +466,9 @@ fun Player.getCurrentCreditAsFlow(): Flow = callbackFlow { } /** - * @return Get the current [PlaybackMetrics] as a [Flow]. + * Collects the [current playback metrics][PillarboxExoPlayer.getCurrentMetrics] as a [Flow]. + * + * @return A [Flow] emitting the current metrics. */ fun PillarboxExoPlayer.currentMetricsAsFlow(): Flow = callbackFlow { val listener = object : Listener { @@ -451,6 +498,6 @@ private fun Tracks.getVideoAspectRatioOrNull(): Float? { } /** - * Default update interval. + * The default interval between [Flow] emissions. */ val DefaultUpdateInterval = 1.seconds diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PreloadConfiguration.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PreloadConfiguration.kt index 85a45ec53..ebdc67e5b 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PreloadConfiguration.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PreloadConfiguration.kt @@ -9,8 +9,10 @@ import androidx.media3.exoplayer.ExoPlayer.PreloadConfiguration import kotlin.time.Duration /** - * @param targetPreloadDuration The target duration to preload or `null` to disable preloading. - * @return [PreloadConfiguration] + * Creates a [PreloadConfiguration] instance using a [Duration]. + * + * @param targetPreloadDuration The target duration to preload. If `null`, preloading will be disabled. + * @return A [PreloadConfiguration] instance with the specified preload duration. */ fun PreloadConfiguration(targetPreloadDuration: Duration?): PreloadConfiguration { return PreloadConfiguration(targetPreloadDuration?.inWholeMicroseconds ?: C.TIME_UNSET) From c8a116aef905051313d7339be318bb3b7e22ab55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Muller?= Date: Wed, 27 Nov 2024 17:32:23 +0100 Subject: [PATCH 17/17] Fix path to the `build.yml` workflow --- docs/CONTINUOUS_INTEGRATION.md | 2 +- docs/README.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/CONTINUOUS_INTEGRATION.md b/docs/CONTINUOUS_INTEGRATION.md index eef63b091..77d92b66f 100644 --- a/docs/CONTINUOUS_INTEGRATION.md +++ b/docs/CONTINUOUS_INTEGRATION.md @@ -4,7 +4,7 @@ The project provides support for continuous integration with GitHub Actions. ## Code quality checks -Every time a Pull Request is made or something is pushed to the `main` branch, the [`build.yml`](https://github.com/SRGSSR/pillarbox-android/blob/main/.github/workflows/build.yml) +Every time a Pull Request is made or something is pushed to the `main` branch, the [`quality.yml`](https://github.com/SRGSSR/pillarbox-android/blob/main/.github/workflows/quality.yml) workflow is triggered by GitHub Actions. It checks that the project builds on various platforms, runs code linters, dependencies check and finally run tests. Result are posted directly in the Pull Request. diff --git a/docs/README.md b/docs/README.md index 7f2fea425..c494f01a3 100644 --- a/docs/README.md +++ b/docs/README.md @@ -4,10 +4,10 @@ [![Last release](https://img.shields.io/github/v/release/SRGSSR/pillarbox-android?label=Release)](https://github.com/SRGSSR/pillarbox-android/releases) [![Android min SDK](https://img.shields.io/badge/Android-21%2B-34A853)](https://github.com/SRGSSR/pillarbox-android) -[![Build status](https://img.shields.io/github/actions/workflow/status/SRGSSR/pillarbox-android/build.yml?label=Build)](https://github.com/SRGSSR/pillarbox-android/actions/workflows/build.yml) +[![Build status](https://img.shields.io/github/actions/workflow/status/SRGSSR/pillarbox-android/quality.yml?label=Build)](https://github.com/SRGSSR/pillarbox-android/actions/workflows/quality.yml) [![License](https://img.shields.io/github/license/SRGSSR/pillarbox-android?label=License)](https://github.com/SRGSSR/pillarbox-android/blob/main/LICENSE) -Pillarbox is the modern SRG SSR multimedia player ecosystem, built on top of [AndroidX Media3](https://developer.android.com/media/media3). +Pillarbox is the modern SRG SSR multimedia player ecosystem, built on top of [AndroidX Media3](https://developer.android.com/media/media3). Pillarbox has been designed with robustness, flexibility, and efficiency in mind, with full customization of: - Metadata and asset URL retrieval. - Asset resource loading, including support for Widevine and PlayReady.