Skip to content

Commit

Permalink
Merge pull request #67 from bitmovin/release/2.4.0
Browse files Browse the repository at this point in the history
Release `2.4.0`
  • Loading branch information
strangesource authored Jun 6, 2024
2 parents 388ca19 + f4c4db1 commit f0b8619
Show file tree
Hide file tree
Showing 17 changed files with 573 additions and 273 deletions.
20 changes: 20 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: CI

on:
pull_request:
push:
branches:
- main

jobs:
build:
name: Build
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: '17'
- run: ./gradlew build
21 changes: 20 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,26 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]
## 2.4.0 - 2024-06-06
### Added
- Ad analytics for ad event reporting

### Changed
- Updated Bitmovin Player to `3.71.0`
- Updated IMA SDK to `3.31.0`
- Updated conviva-core to `4.0.37`
- Increased minimum required `compileSdk` version to `34`
- Increased `compileSdk` and `targetSdkVersion` to `34`
- Increased `minSdkVersion` to `19`
- Ad break started and ended is now reported in `PlayerEvent.AdBreakStarted` and `PlayerEvent.AdBreakFinished`
- Updated Kotlin to `1.9.23`
- Updated Gradle wrapper to `8.2` and AGP to `8.2.2`

### Removed
- Custom event for `AdSkipped` and `AdError`. Replaced by Conviva build in tracking

### Fixed
- The pom file now also includes the `com.bitmovin.player` dependency which was missing before

## 2.3.0 - 2024-05-21
### Added
Expand Down
3 changes: 2 additions & 1 deletion ConvivaExampleApp/build.gradle
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
apply plugin: 'com.android.application'

android {
compileSdkVersion rootProject.compileSdkVersion
compileSdk rootProject.compileSdkVersion

defaultConfig {
applicationId "com.bitmovin.analytics.convivaanalyticsexample"
minSdkVersion rootProject.minSdkVersion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@
import android.widget.LinearLayout;
import android.widget.Switch;

import com.bitmovin.analytics.conviva.ConvivaAnalyticsIntegration;;
import com.bitmovin.analytics.conviva.ConvivaAnalyticsIntegration;
import com.bitmovin.analytics.conviva.ConvivaConfig;
import com.bitmovin.analytics.conviva.MetadataOverrides;
import com.bitmovin.player.api.Player;
import com.bitmovin.player.PlayerView;
import com.bitmovin.player.api.PlayerBuilder;
import com.bitmovin.player.api.source.SourceConfig;
import com.bitmovin.player.api.PlayerConfig;
import com.bitmovin.player.api.advertising.AdItem;
Expand Down Expand Up @@ -63,7 +64,10 @@ protected void onCreate(Bundle savedInstanceState) {
}

protected void setupBitmovinPlayer() {
this.bitmovinPlayer = Player.create(this, buildPlayerConfiguration());
this.bitmovinPlayer = new PlayerBuilder(this)
.setPlayerConfig(buildPlayerConfiguration())
.disableAnalytics()
.build();
this.bitmovinPlayerView = new PlayerView(this, this.bitmovinPlayer);

LinearLayout playerUIView = this.findViewById(R.id.bitmovinPlayerUIView);
Expand Down Expand Up @@ -99,6 +103,8 @@ protected void setupBitmovinPlayer() {
customTags.put("custom_tag", "Episode");
metadata.setCustom(customTags);

metadata.setImaSdkVersion("3.31.0");

convivaAnalyticsIntegration.updateContentMetadata(metadata);

// load source using the created source configuration
Expand Down
5 changes: 2 additions & 3 deletions ConvivaTestApp/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ plugins {
}

android {
compileSdkVersion rootProject.compileSdkVersion
buildToolsVersion "30.0.3"
compileSdk rootProject.compileSdkVersion

defaultConfig {
applicationId "com.bitmovin.analytics.conviva.testapp"
Expand Down Expand Up @@ -38,7 +37,7 @@ android {
}

dependencies {
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation bitmovinPlayerDependencies.bitmovinPlayer

implementation "com.google.ads.interactivemedia.v3:interactivemedia:$googleImaSdk" // only needed if ads are used:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package com.bitmovin.analytics.conviva.testapp

import android.os.Handler
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import com.bitmovin.analytics.conviva.ConvivaAnalyticsIntegration
import com.bitmovin.analytics.conviva.ConvivaConfig
import com.bitmovin.analytics.conviva.MetadataOverrides
import com.bitmovin.player.api.PlaybackConfig
import com.bitmovin.player.api.Player
import com.bitmovin.player.api.PlayerConfig
import com.bitmovin.player.api.advertising.AdItem
import com.bitmovin.player.api.advertising.AdSource
import com.bitmovin.player.api.advertising.AdSourceType
import com.bitmovin.player.api.advertising.AdvertisingConfig
import com.bitmovin.player.api.analytics.AnalyticsPlayerConfig
import com.bitmovin.player.api.event.Event
import com.bitmovin.player.api.event.EventEmitter
import com.bitmovin.player.api.event.PlayerEvent
import com.bitmovin.player.api.event.on
import com.bitmovin.player.api.source.SourceConfig
import com.bitmovin.player.api.source.SourceType
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import org.junit.Test
import org.junit.runner.RunWith
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine

private const val BITMOVIN_PLAYER_LICENSE_KEY = "YOUR_LICENSE_KEY"
private const val CONVIVA_CUSTOMER_KEY = "YOUR-CUSTOMER-KEY"
private const val CONVIVA_GATEWAY_URL = "YOUR-GATEWAY-URL"

private const val VMAP_PREROLL_SINGLE_TAG = "https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/124319096/external/ad_rule_samples&ciu_szs=300x250&ad_rule=1&impl=s&gdfp_req=1&env=vp&output=vmap&unviewed_position_start=1&cust_params=deployment%3Ddevsite%26sample_ar%3Dpreonly&cmsid=496&vid=short_onecue&correlator="
private val ART_OF_MOTION_DASH = SourceConfig(
url = "https://bitmovin-a.akamaihd.net/content/MI201109210084_1/mpds/f08e80da-bf1d-4e3d-8899-f0f6155f6efa.mpd",
type = SourceType.Dash,
title = "Art of Motion Test Stream",
)

/**
* This test class does not verify any specific behavior, but rather can be used to validate the
* integration against the [Conviva Touchstone integration test tool](https://touchstone.conviva.com/).
*/
@RunWith(AndroidJUnit4::class)
class AdvertisingTests {
/**
* Plays the first 5 seconds of a stream with a VMAP ad that includes a single pre-roll with a
* attached [ConvivaAnalyticsIntegration].
* Ad playback is paused and resumed to test for according events.
*/
@Test
fun reports_ad_analytics_for_IMA_pre_roll_ad() {
val context = InstrumentationRegistry.getInstrumentation().targetContext
val mainHandler = Handler(context.mainLooper)
val player = mainHandler.postWaiting {
val player = Player(
context,
PlayerConfig(
key = BITMOVIN_PLAYER_LICENSE_KEY,
advertisingConfig = AdvertisingConfig(
AdItem(AdSource(AdSourceType.Ima, VMAP_PREROLL_SINGLE_TAG)),
),
playbackConfig = PlaybackConfig(
isAutoplayEnabled = true,
),
),
analyticsConfig = AnalyticsPlayerConfig.Disabled,
)
val convivaAnalyticsIntegration = ConvivaAnalyticsIntegration(
player,
CONVIVA_CUSTOMER_KEY,
context,
ConvivaConfig().apply {
isDebugLoggingEnabled = true
gatewayUrl = CONVIVA_GATEWAY_URL
},
)

convivaAnalyticsIntegration.updateContentMetadata(
MetadataOverrides()
.apply {
applicationName = "Bitmovin Android Conviva integration example app"
viewerId = "testViewerId"
}
)
player
}

mainHandler.postWaiting { player.load(ART_OF_MOTION_DASH) }
player.expectEvent<PlayerEvent.TimeChanged> { it.time > 5.0 }

// pause player for a second and resume playback
mainHandler.postWaiting { player.pause() }
runBlocking { delay(1000) }
mainHandler.postWaiting { player.play() }

// wait for the ad break to finish and play main content for five more seconds
player.expectEvent<PlayerEvent.AdBreakFinished>()
player.expectEvent<PlayerEvent.TimeChanged> { it.time > 5.0 }

mainHandler.postWaiting { player.destroy() }
runBlocking { delay(1000) }
}
}

/**
* Subscribes to an [Event] on the [Player] and suspends until the event is emitted.
* Optionally a [condition] can be provided to filter the emitted events.
*/
private inline fun <reified T : Event> EventEmitter<Event>.expectEvent(
crossinline condition: (T) -> Boolean = { true }
) = runBlocking {
suspendCoroutine { continuation ->
lateinit var action: ((T) -> Unit)
action = {
if (condition(it)) {
off(action)
continuation.resume(Unit)
}
}
on<T>(action)
}
}

/**
* Posts a [block] of code to the main thread and suspends until it is executed.
*/
private inline fun <T> Handler.postWaiting(crossinline block: () -> T) = runBlocking {
suspendCoroutine { continuation ->
post { continuation.resume(block()) }
}
}
Loading

0 comments on commit f0b8619

Please sign in to comment.