Skip to content

Commit

Permalink
feat: Bandwidth/Request Metrics (#12)
Browse files Browse the repository at this point in the history
  • Loading branch information
daytime-em committed Jun 28, 2023
1 parent 94496c3 commit e372d1c
Show file tree
Hide file tree
Showing 9 changed files with 704 additions and 14 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.mux.stats.muxdatasdkformedia3

object Constants {
const val MUX_DATA_ENV_KEY = "YOUR KEY HERE"
const val MUX_DATA_ENV_KEY = "YOUR ENV KEY HERE"
const val VOD_TEST_URL_STEVE = "http://qthttp.apple.com.edgesuite.net/1010qwoeiuryfg/sl.m3u8"
const val VOD_TEST_URL_DRAGON_WARRIOR_LADY =
"https://bitdash-a.akamaihd.net/content/sintel/hls/playlist.m3u8"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,4 +110,4 @@ class BackgroundPlayService : MediaSessionService() {
PlayableVideo("sintel", Constants.VOD_TEST_URL_DRAGON_WARRIOR_LADY, "Sintel"),
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class BasicPlayerActivity : AppCompatActivity() {

private lateinit var view: ActivityPlayerBinding
private var player: Player? = null
private var muxStats: MuxStatsSdkMedia3<*>? = null
private var muxStats: MuxStatsSdkMedia3<ExoPlayer>? = null

@OptIn(UnstableApi::class)
override fun onCreate(savedInstanceState: Bundle?) {
Expand Down Expand Up @@ -68,7 +68,7 @@ class BasicPlayerActivity : AppCompatActivity() {
muxStats?.release()
}

private fun monitorPlayer(player: Player): MuxStatsSdkMedia3<*> {
private fun monitorPlayer(player: ExoPlayer): MuxStatsSdkMedia3<ExoPlayer> {
// You can add your own data to a View, which will override any data we collect
val customerData = CustomerData(
CustomerPlayerData().apply { },
Expand All @@ -86,7 +86,7 @@ class BasicPlayerActivity : AppCompatActivity() {
)
}

private fun createPlayer(): Player {
private fun createPlayer(): ExoPlayer {
return ExoPlayer.Builder(this)
.build().apply {
addListener(object : Player.Listener {
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ allprojects {
project.ext {
media3Version = '1.0.2'
// coreVersion = "0.7.4"
coreVersion = 'dev-feat-dropped-frames-count-578b47c'
coreVersion = 'dev-rendition-list-5b678be'
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,15 @@ import androidx.media3.common.util.UnstableApi
import androidx.media3.exoplayer.DecoderReuseEvaluation
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.exoplayer.analytics.AnalyticsListener
import androidx.media3.exoplayer.source.LoadEventInfo
import androidx.media3.exoplayer.source.MediaLoadData
import com.mux.android.util.weak
import com.mux.stats.sdk.core.util.MuxLogger
import com.mux.stats.sdk.muxstats.bandwidth.BandwidthMetricDispatcher
import com.mux.stats.sdk.muxstats.bandwidth.TrackedHeader
import com.mux.stats.sdk.muxstats.internal.createExoSessionDataBinding
import java.io.IOException
import java.util.regex.Pattern

class ExoPlayerBinding : MuxPlayerAdapter.PlayerBinding<ExoPlayer> {

Expand All @@ -21,7 +27,20 @@ class ExoPlayerBinding : MuxPlayerAdapter.PlayerBinding<ExoPlayer> {
private var listener: MuxAnalyticsListener? = null

override fun bindPlayer(player: ExoPlayer, collector: MuxStateCollector) {
listener = MuxAnalyticsListener(player, collector).also { player.addAnalyticsListener(it) }
listener = MuxAnalyticsListener(
player = player,
collector = collector,
bandwidthMetrics = BandwidthMetricDispatcher(
player = player,
collector = collector,
trackedResponseHeaders = listOf(
TrackedHeader.ExactlyIgnoreCase("x-cdn"),
TrackedHeader.ExactlyIgnoreCase("content-type"),
TrackedHeader.ExactlyIgnoreCase("x-request-id"),
TrackedHeader.Matching(Pattern.compile("^x-litix-.*", Pattern.CASE_INSENSITIVE))
)
)
).also { player.addAnalyticsListener(it) }

// Also delegate to sub-bindings
sessionDataBinding.bindPlayer(player, collector)
Expand All @@ -35,12 +54,23 @@ class ExoPlayerBinding : MuxPlayerAdapter.PlayerBinding<ExoPlayer> {
// Also delegate to sub-bindings
sessionDataBinding.unbindPlayer(player, collector)
}

companion object {
@Suppress("unused")
private const val TAG = "ExoPlayerBinding"
}

}

@OptIn(UnstableApi::class)
private class MuxAnalyticsListener(player: ExoPlayer, val collector: MuxStateCollector)
: AnalyticsListener {
private val player by weak(player)
private class MuxAnalyticsListener(
player: ExoPlayer,
bandwidthMetrics: BandwidthMetricDispatcher,
val collector: MuxStateCollector,
) : AnalyticsListener {

private val bandwidthMetrics by weak(bandwidthMetrics)
private val player by weak(player)

override fun onPlaybackStateChanged(eventTime: AnalyticsListener.EventTime, state: Int) {
// query playWhenReady for consistency. The order of execution between this callback and
Expand Down Expand Up @@ -69,19 +99,29 @@ private class MuxAnalyticsListener(player: ExoPlayer, val collector: MuxStateCol
format: Format,
decoderReuseEvaluation: DecoderReuseEvaluation?
) {
MuxLogger.d(
TAG, "onVideoInputFormatChanged: new format: bitrate ${format.bitrate}" +
" and frameRate ${format.frameRate} "
)
val cleanBitrate = format.bitrate.takeIf { it >= 0 } ?: 0
val cleanFrameRate = format.frameRate.takeIf { it >= 0 } ?: 0F

collector.renditionChange(
advertisedBitrate = format.averageBitrate,
advertisedFrameRate = format.frameRate,
advertisedBitrate = cleanBitrate,
advertisedFrameRate = cleanFrameRate,
sourceWidth = format.width,
sourceHeight = format.height
)
}

override fun onTracksChanged(eventTime: AnalyticsListener.EventTime, tracks: Tracks) {
MuxLogger.d("ExoPlayerBinding", "onTracksChanged")

player?.let {
collector.watchPlayerPos(it)
collector.mediaHasVideoTrack = tracks.hasAtLeastOneVideoTrack()
}
bandwidthMetrics?.onTracksChanged(tracks)
}

override fun onDownstreamFormatChanged(
Expand All @@ -93,7 +133,11 @@ private class MuxAnalyticsListener(player: ExoPlayer, val collector: MuxStateCol
}
}

override fun onRenderedFirstFrame(eventTime: AnalyticsListener.EventTime, output: Any, renderTimeMs: Long) {
override fun onRenderedFirstFrame(
eventTime: AnalyticsListener.EventTime,
output: Any,
renderTimeMs: Long
) {
collector.onFirstFrameRendered()
}

Expand All @@ -112,4 +156,76 @@ private class MuxAnalyticsListener(player: ExoPlayer, val collector: MuxStateCol
collector.sourceWidth = videoSize.width
collector.sourceHeight = videoSize.height
}

override fun onLoadError(
eventTime: AnalyticsListener.EventTime,
loadEventInfo: LoadEventInfo,
mediaLoadData: MediaLoadData,
error: IOException,
wasCanceled: Boolean
) {
bandwidthMetrics?.onLoadError(
loadTaskId = loadEventInfo.loadTaskId,
segmentUrl = loadEventInfo.uri.path,
e = error
)
}

override fun onLoadCanceled(
eventTime: AnalyticsListener.EventTime,
loadEventInfo: LoadEventInfo,
mediaLoadData: MediaLoadData
) {
bandwidthMetrics?.onLoadCanceled(
loadTaskId = loadEventInfo.loadTaskId,
segmentUrl = loadEventInfo.uri.path,
headers = loadEventInfo.responseHeaders
)
}

override fun onLoadStarted(
eventTime: AnalyticsListener.EventTime,
loadEventInfo: LoadEventInfo,
mediaLoadData: MediaLoadData
) {
var segmentMimeType = "unknown"
var segmentWidth = 0
var segmentHeight = 0

mediaLoadData.trackFormat?.let { format ->
format.sampleMimeType?.let { segmentMimeType = it }
segmentWidth = format.width
segmentHeight = format.height
}
bandwidthMetrics?.onLoadStarted(
loadEventInfo.loadTaskId, mediaLoadData.mediaStartTimeMs,
mediaLoadData.mediaEndTimeMs, loadEventInfo.uri.path, mediaLoadData.dataType,
loadEventInfo.uri.host, segmentMimeType, segmentWidth, segmentHeight
)
}

override fun onLoadCompleted(
eventTime: AnalyticsListener.EventTime,
loadEventInfo: LoadEventInfo,
mediaLoadData: MediaLoadData
) {
@Suppress("SENSELESS_COMPARISON")
if (loadEventInfo.uri != null) {
bandwidthMetrics?.onLoadCompleted(
loadEventInfo.loadTaskId,
loadEventInfo.uri.path,
loadEventInfo.bytesLoaded,
mediaLoadData.trackFormat,
loadEventInfo.responseHeaders
)
}
} // fun onLoadCompleted

companion object {
private const val TAG = "ExoPlayerBinding"
}

init {
MuxLogger.d(TAG, "Listening to ExoPlayer $player")
}
}
Loading

0 comments on commit e372d1c

Please sign in to comment.