Skip to content

Commit 76e51c9

Browse files
daytime-emweb-flow
andauthored
Releases/v1.4.5 (#77)
## 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]>
1 parent 4b5b217 commit 76e51c9

File tree

8 files changed

+136
-72
lines changed

8 files changed

+136
-72
lines changed

app/build.gradle.kts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ plugins {
55

66
android {
77
namespace = "com.mux.app"
8-
compileSdk = 34
8+
compileSdk = 35
99

1010
defaultConfig {
1111
applicationId = "com.mux.app"
1212
minSdk = 24
13-
targetSdk = 34
13+
targetSdk = 35
1414
versionCode = 1
1515
versionName = "1.0"
1616

@@ -36,11 +36,11 @@ dependencies {
3636

3737
project(":library")
3838

39-
implementation("androidx.core:core-ktx:1.13.1")
39+
implementation("androidx.core:core-ktx:1.15.0")
4040
implementation("androidx.appcompat:appcompat:1.7.0")
4141
implementation("com.google.android.material:material:1.12.0")
42-
implementation("androidx.activity:activity:1.8.0")
43-
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
42+
implementation("androidx.activity:activity:1.9.3")
43+
implementation("androidx.constraintlayout:constraintlayout:2.2.0")
4444
testImplementation("junit:junit:4.13.2")
4545
androidTestImplementation("androidx.test.ext:junit:1.2.1")
4646
androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1")

app/src/androidTest/java/com/mux/app/ExampleInstrumentedTest.kt

Lines changed: 0 additions & 24 deletions
This file was deleted.

app/src/test/java/com/mux/app/ExampleUnitTest.kt

Lines changed: 0 additions & 17 deletions
This file was deleted.

library/build.gradle

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,9 @@ muxDistribution {
7676
dependencies {
7777
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.1"
7878

79-
api "com.mux:stats.muxcore:8.1.3"
79+
api "com.mux:stats.muxcore:8.1.4"
8080

81+
testImplementation 'androidx.test.ext:junit-ktx:1.2.1'
8182
testImplementation 'junit:junit:4.13.2'
8283
testImplementation 'androidx.test.ext:junit:1.2.1'
8384
testImplementation "io.mockk:mockk:1.13.9"

library/src/androidTest/java/com/mux/stats/sdk/muxstats/ExampleInstrumentedTest.kt

Lines changed: 0 additions & 24 deletions
This file was deleted.

library/src/main/java/com/mux/stats/sdk/muxstats/MuxStateCollector.kt

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.mux.stats.sdk.muxstats
22

3+
import android.util.Log
34
import com.mux.android.util.logTag
45
import com.mux.android.util.noneOf
56
import com.mux.android.util.oneOf
@@ -10,6 +11,7 @@ import com.mux.stats.sdk.core.events.InternalErrorEvent
1011
import com.mux.stats.sdk.core.events.playback.*
1112
import com.mux.stats.sdk.core.model.BandwidthMetricData
1213
import com.mux.stats.sdk.core.model.CustomerVideoData
14+
import com.mux.stats.sdk.core.model.PlayerData
1315
import com.mux.stats.sdk.core.model.SessionTag
1416
import com.mux.stats.sdk.core.model.VideoData
1517
import com.mux.stats.sdk.core.util.MuxLogger
@@ -442,8 +444,18 @@ open class MuxStateCollector(
442444
reset()
443445
}
444446

447+
/**
448+
* Set to true if we get a main-content format change during a period when we shouldn't be getting
449+
* one (eg, during ad breaks). In this case, the rendition info will be sent after the ad break
450+
* ends
451+
*/
452+
private var contentRenditionDeferred: Boolean = false
453+
445454
/**
446455
* Call when the currently-playing rendition changes.
456+
*
457+
* Expects only to be called for changes to the rendition of the main content being played. Ad
458+
* sizes should not be reported, even SSAI ads or segmented CSAI ads
447459
*/
448460
@Suppress("unused")
449461
fun renditionChange(
@@ -457,6 +469,12 @@ open class MuxStateCollector(
457469
this.sourceWidth = sourceWidth
458470
this.sourceHeight = sourceHeight
459471

472+
if (_playerState == MuxPlayerState.PLAYING_ADS) {
473+
// we have to save this one for after the ad break
474+
contentRenditionDeferred = true
475+
return
476+
}
477+
460478
dispatch(RenditionChangeEvent(null))
461479
}
462480

@@ -484,6 +502,12 @@ open class MuxStateCollector(
484502
fun finishedPlayingAds() {
485503
_playerState = MuxPlayerState.FINISHED_PLAYING_ADS
486504

505+
if (contentRenditionDeferred) {
506+
MuxLogger.d("MuxStateCollector", "sending deferred content 'renditionchange'")
507+
contentRenditionDeferred = false
508+
dispatch(RenditionChangeEvent(null))
509+
}
510+
487511
// players allow seeking out of ads.
488512
// If playback follows, the data sdk also needs to call playing()
489513
if (seekingInProgress) {
@@ -547,6 +571,7 @@ open class MuxStateCollector(
547571
allowedHeaders.clear()
548572
droppedFrames = 0
549573
muxStats.setDroppedFramesCount(0)
574+
contentRenditionDeferred = false
550575
}
551576

552577
@JvmSynthetic

library/src/test/java/com/mux/core_android/StateCollectorTests.kt

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import org.junit.Assert.assertEquals
1818
import org.junit.Assert.assertNotEquals
1919
import org.junit.Before
2020
import org.junit.Test
21+
import kotlin.math.exp
2122

2223
class StateCollectorTests : AbsRobolectricTest() {
2324

@@ -352,4 +353,105 @@ class StateCollectorTests : AbsRobolectricTest() {
352353
mockMuxStats.setDroppedFramesCount(0)
353354
}
354355
}
356+
357+
@Test
358+
fun testRenditionChangeDuringAd() {
359+
val expected = 2000;
360+
361+
eventDispatcher.dispatch(AdBreakStartEvent(null))
362+
stateCollector.playingAds()
363+
stateCollector.renditionChange(
364+
advertisedBitrate = 1000,
365+
advertisedFrameRate = 1000f,
366+
sourceHeight = 1000,
367+
sourceWidth = 1000
368+
)
369+
stateCollector.renditionChange(
370+
advertisedFrameRate = expected.toFloat(),
371+
advertisedBitrate = expected,
372+
sourceWidth = expected,
373+
sourceHeight = expected,
374+
)
375+
eventDispatcher.dispatch(AdBreakEndEvent(null))
376+
stateCollector.finishedPlayingAds()
377+
378+
eventDispatcher.assertHasExactlyThese(listOf(
379+
AdBreakStartEvent(null), AdBreakEndEvent(null), RenditionChangeEvent(null)
380+
))
381+
382+
assertEquals(
383+
"Values from intermediate rendition changes should be skipped",
384+
expected, stateCollector.sourceAdvertisedBitrate
385+
)
386+
assertEquals(
387+
"Values from intermediate rendition changes should be skipped",
388+
expected.toFloat(), stateCollector.sourceAdvertisedFrameRate.toFloat()
389+
)
390+
assertEquals(
391+
"Values from intermediate rendition changes should be skipped",
392+
expected, stateCollector.sourceWidth
393+
)
394+
assertEquals(
395+
"Values from intermediate rendition changes should be skipped",
396+
expected, stateCollector.sourceHeight
397+
)
398+
}
399+
400+
@Test
401+
fun testRenditionChangeNotDuringAd() {
402+
val finalExpectedValue = 2000;
403+
404+
stateCollector.play()
405+
stateCollector.playing()
406+
// this method under test and should emit one 'renditionchange'
407+
stateCollector.renditionChange(
408+
advertisedBitrate = 1000,
409+
advertisedFrameRate = 1000f,
410+
sourceHeight = 1000,
411+
sourceWidth = 1000
412+
)
413+
// this method under test and should emit no events
414+
stateCollector.finishedPlayingAds()
415+
416+
stateCollector.pause();
417+
stateCollector.playing();
418+
419+
// this method under test and should emit one 'renditionchange'
420+
stateCollector.renditionChange(
421+
advertisedBitrate = 3000,
422+
advertisedFrameRate = 3000f,
423+
sourceHeight = 3000,
424+
sourceWidth = 3000,
425+
)
426+
// this method under test and should emit one 'renditionchange'
427+
stateCollector.renditionChange(
428+
advertisedBitrate = finalExpectedValue,
429+
advertisedFrameRate = finalExpectedValue.toFloat(),
430+
sourceHeight = finalExpectedValue,
431+
sourceWidth = finalExpectedValue,
432+
)
433+
434+
eventDispatcher.assertHasExactlyThese(listOf(
435+
PlayEvent(null), PlayingEvent(null), RenditionChangeEvent(null),
436+
PauseEvent(null), PlayEvent(null), PlayingEvent(null),
437+
RenditionChangeEvent(null), RenditionChangeEvent(null)
438+
))
439+
440+
assertEquals(
441+
"Values from intermediate rendition changes should be skipped",
442+
finalExpectedValue, stateCollector.sourceAdvertisedBitrate
443+
)
444+
assertEquals(
445+
"Values from intermediate rendition changes should be skipped",
446+
finalExpectedValue.toFloat(), stateCollector.sourceAdvertisedFrameRate.toFloat()
447+
)
448+
assertEquals(
449+
"Values from intermediate rendition changes should be skipped",
450+
finalExpectedValue, stateCollector.sourceWidth
451+
)
452+
assertEquals(
453+
"Values from intermediate rendition changes should be skipped",
454+
finalExpectedValue, stateCollector.sourceHeight
455+
)
456+
}
355457
}

library/src/test/java/com/mux/core_android/test/tools/testdoubles/FakeEventDispatcher.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,8 @@ class FakeEventDispatcher : IEventDispatcher {
7979
}
8080

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

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

0 commit comments

Comments
 (0)