Skip to content

Commit

Permalink
Releases/v1.4.5 (#77)
Browse files Browse the repository at this point in the history
## Fixes
* fix: Defer content rendition changes during ads  (#76)

### Internal lib updates
* update `stats.muxcore` to v8.1.4



Co-authored-by: Emily Dixon <[email protected]>
Co-authored-by: GitHub <[email protected]>
  • Loading branch information
daytime-em and web-flow authored Dec 13, 2024
1 parent 4b5b217 commit 76e51c9
Show file tree
Hide file tree
Showing 8 changed files with 136 additions and 72 deletions.
10 changes: 5 additions & 5 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ plugins {

android {
namespace = "com.mux.app"
compileSdk = 34
compileSdk = 35

defaultConfig {
applicationId = "com.mux.app"
minSdk = 24
targetSdk = 34
targetSdk = 35
versionCode = 1
versionName = "1.0"

Expand All @@ -36,11 +36,11 @@ dependencies {

project(":library")

implementation("androidx.core:core-ktx:1.13.1")
implementation("androidx.core:core-ktx:1.15.0")
implementation("androidx.appcompat:appcompat:1.7.0")
implementation("com.google.android.material:material:1.12.0")
implementation("androidx.activity:activity:1.8.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("androidx.activity:activity:1.9.3")
implementation("androidx.constraintlayout:constraintlayout:2.2.0")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.2.1")
androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1")
Expand Down
24 changes: 0 additions & 24 deletions app/src/androidTest/java/com/mux/app/ExampleInstrumentedTest.kt

This file was deleted.

17 changes: 0 additions & 17 deletions app/src/test/java/com/mux/app/ExampleUnitTest.kt

This file was deleted.

3 changes: 2 additions & 1 deletion library/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,9 @@ muxDistribution {
dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.1"

api "com.mux:stats.muxcore:8.1.3"
api "com.mux:stats.muxcore:8.1.4"

testImplementation 'androidx.test.ext:junit-ktx:1.2.1'
testImplementation 'junit:junit:4.13.2'
testImplementation 'androidx.test.ext:junit:1.2.1'
testImplementation "io.mockk:mockk:1.13.9"
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.mux.stats.sdk.muxstats

import android.util.Log
import com.mux.android.util.logTag
import com.mux.android.util.noneOf
import com.mux.android.util.oneOf
Expand All @@ -10,6 +11,7 @@ import com.mux.stats.sdk.core.events.InternalErrorEvent
import com.mux.stats.sdk.core.events.playback.*
import com.mux.stats.sdk.core.model.BandwidthMetricData
import com.mux.stats.sdk.core.model.CustomerVideoData
import com.mux.stats.sdk.core.model.PlayerData
import com.mux.stats.sdk.core.model.SessionTag
import com.mux.stats.sdk.core.model.VideoData
import com.mux.stats.sdk.core.util.MuxLogger
Expand Down Expand Up @@ -442,8 +444,18 @@ open class MuxStateCollector(
reset()
}

/**
* Set to true if we get a main-content format change during a period when we shouldn't be getting
* one (eg, during ad breaks). In this case, the rendition info will be sent after the ad break
* ends
*/
private var contentRenditionDeferred: Boolean = false

/**
* Call when the currently-playing rendition changes.
*
* Expects only to be called for changes to the rendition of the main content being played. Ad
* sizes should not be reported, even SSAI ads or segmented CSAI ads
*/
@Suppress("unused")
fun renditionChange(
Expand All @@ -457,6 +469,12 @@ open class MuxStateCollector(
this.sourceWidth = sourceWidth
this.sourceHeight = sourceHeight

if (_playerState == MuxPlayerState.PLAYING_ADS) {
// we have to save this one for after the ad break
contentRenditionDeferred = true
return
}

dispatch(RenditionChangeEvent(null))
}

Expand Down Expand Up @@ -484,6 +502,12 @@ open class MuxStateCollector(
fun finishedPlayingAds() {
_playerState = MuxPlayerState.FINISHED_PLAYING_ADS

if (contentRenditionDeferred) {
MuxLogger.d("MuxStateCollector", "sending deferred content 'renditionchange'")
contentRenditionDeferred = false
dispatch(RenditionChangeEvent(null))
}

// players allow seeking out of ads.
// If playback follows, the data sdk also needs to call playing()
if (seekingInProgress) {
Expand Down Expand Up @@ -547,6 +571,7 @@ open class MuxStateCollector(
allowedHeaders.clear()
droppedFrames = 0
muxStats.setDroppedFramesCount(0)
contentRenditionDeferred = false
}

@JvmSynthetic
Expand Down
102 changes: 102 additions & 0 deletions library/src/test/java/com/mux/core_android/StateCollectorTests.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotEquals
import org.junit.Before
import org.junit.Test
import kotlin.math.exp

class StateCollectorTests : AbsRobolectricTest() {

Expand Down Expand Up @@ -352,4 +353,105 @@ class StateCollectorTests : AbsRobolectricTest() {
mockMuxStats.setDroppedFramesCount(0)
}
}

@Test
fun testRenditionChangeDuringAd() {
val expected = 2000;

eventDispatcher.dispatch(AdBreakStartEvent(null))
stateCollector.playingAds()
stateCollector.renditionChange(
advertisedBitrate = 1000,
advertisedFrameRate = 1000f,
sourceHeight = 1000,
sourceWidth = 1000
)
stateCollector.renditionChange(
advertisedFrameRate = expected.toFloat(),
advertisedBitrate = expected,
sourceWidth = expected,
sourceHeight = expected,
)
eventDispatcher.dispatch(AdBreakEndEvent(null))
stateCollector.finishedPlayingAds()

eventDispatcher.assertHasExactlyThese(listOf(
AdBreakStartEvent(null), AdBreakEndEvent(null), RenditionChangeEvent(null)
))

assertEquals(
"Values from intermediate rendition changes should be skipped",
expected, stateCollector.sourceAdvertisedBitrate
)
assertEquals(
"Values from intermediate rendition changes should be skipped",
expected.toFloat(), stateCollector.sourceAdvertisedFrameRate.toFloat()
)
assertEquals(
"Values from intermediate rendition changes should be skipped",
expected, stateCollector.sourceWidth
)
assertEquals(
"Values from intermediate rendition changes should be skipped",
expected, stateCollector.sourceHeight
)
}

@Test
fun testRenditionChangeNotDuringAd() {
val finalExpectedValue = 2000;

stateCollector.play()
stateCollector.playing()
// this method under test and should emit one 'renditionchange'
stateCollector.renditionChange(
advertisedBitrate = 1000,
advertisedFrameRate = 1000f,
sourceHeight = 1000,
sourceWidth = 1000
)
// this method under test and should emit no events
stateCollector.finishedPlayingAds()

stateCollector.pause();
stateCollector.playing();

// this method under test and should emit one 'renditionchange'
stateCollector.renditionChange(
advertisedBitrate = 3000,
advertisedFrameRate = 3000f,
sourceHeight = 3000,
sourceWidth = 3000,
)
// this method under test and should emit one 'renditionchange'
stateCollector.renditionChange(
advertisedBitrate = finalExpectedValue,
advertisedFrameRate = finalExpectedValue.toFloat(),
sourceHeight = finalExpectedValue,
sourceWidth = finalExpectedValue,
)

eventDispatcher.assertHasExactlyThese(listOf(
PlayEvent(null), PlayingEvent(null), RenditionChangeEvent(null),
PauseEvent(null), PlayEvent(null), PlayingEvent(null),
RenditionChangeEvent(null), RenditionChangeEvent(null)
))

assertEquals(
"Values from intermediate rendition changes should be skipped",
finalExpectedValue, stateCollector.sourceAdvertisedBitrate
)
assertEquals(
"Values from intermediate rendition changes should be skipped",
finalExpectedValue.toFloat(), stateCollector.sourceAdvertisedFrameRate.toFloat()
)
assertEquals(
"Values from intermediate rendition changes should be skipped",
finalExpectedValue, stateCollector.sourceWidth
)
assertEquals(
"Values from intermediate rendition changes should be skipped",
finalExpectedValue, stateCollector.sourceHeight
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ class FakeEventDispatcher : IEventDispatcher {
}

private fun failAssert(message: String, expected: List<IEvent>, actual: List<IEvent>) {
throw AssertionError("$message:\nActual: $actual\nExpected: $expected")
throw AssertionError("$message:\nActual: ${actual.map { it.type }}" +
"\nExpected: ${expected.map { it.type }}")
}

data class Record(val event: IEvent, val systemTime: Long)
Expand Down

0 comments on commit 76e51c9

Please sign in to comment.