Skip to content

Commit

Permalink
Merge pull request #72 from bitmovin/add-ssai-api
Browse files Browse the repository at this point in the history
Introduce SSAI API
  • Loading branch information
strangesource authored Jul 2, 2024
2 parents ec14690 + 83bae35 commit 9d42de0
Show file tree
Hide file tree
Showing 6 changed files with 620 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.bitmovin.analytics.conviva.ssai;

public enum AdPosition {
PREROLL,
MIDROLL,
POSTROLL;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.bitmovin.analytics.conviva.ssai;

import com.bitmovin.player.api.Player;
import com.conviva.sdk.ConvivaSdkConstants;

public class DefaultPlaybackStateProvider implements PlaybackStateProvider {
private final Player player;

public DefaultPlaybackStateProvider(Player player) {
this.player = player;
}

@Override
public ConvivaSdkConstants.PlayerState getPlayerState() {
ConvivaSdkConstants.PlayerState state;
if (player.isPaused()) {
state = ConvivaSdkConstants.PlayerState.PAUSED;
} else if (player.isStalled()) {
state = ConvivaSdkConstants.PlayerState.BUFFERING;
} else {
state = ConvivaSdkConstants.PlayerState.PLAYING;
}
return state;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package com.bitmovin.analytics.conviva.ssai;

import android.util.Log;

import com.conviva.sdk.ConvivaAdAnalytics;
import com.conviva.sdk.ConvivaSdkConstants;
import com.conviva.sdk.ConvivaVideoAnalytics;

import java.util.HashMap;
import java.util.Map;


public class DefaultSsaiApi implements SsaiApi {
private static final String TAG = "DefaultSsaiApi";
private final ConvivaVideoAnalytics convivaVideoAnalytics;
private final ConvivaAdAnalytics convivaAdAnalytics;
private final PlaybackStateProvider player;

public DefaultSsaiApi(ConvivaVideoAnalytics convivaVideoAnalytics, ConvivaAdAnalytics convivaAdAnalytics, PlaybackStateProvider player) {
this.convivaVideoAnalytics = convivaVideoAnalytics;
this.convivaAdAnalytics = convivaAdAnalytics;
this.player = player;
}

private boolean isAdBreakActive = false;

@Override
public boolean isAdBreakActive() {
return isAdBreakActive;
}

@Override
public void reportAdBreakStarted() {
reportAdBreakStarted(null);
}

public void reset() {
isAdBreakActive = false;
}

@Override
public void reportAdBreakStarted(Map<String, Object> adBreakInfo) {
if (isAdBreakActive) {
Log.d(TAG, "Server side ad break already active");
return;
}
isAdBreakActive = true;
Log.d(TAG, "Server side ad break started");
convivaVideoAnalytics.reportAdBreakStarted(ConvivaSdkConstants.AdPlayer.CONTENT, ConvivaSdkConstants.AdType.SERVER_SIDE, adBreakInfo);
}


@Override
public void reportAdBreakFinished() {
if (!isAdBreakActive) {
Log.d(TAG, "No server side ad break active");
return;
}
isAdBreakActive = false;
Log.d(TAG, "Server side ad break finished");
convivaVideoAnalytics.reportAdBreakEnded();
}


@Override
public void reportAdStarted(AdInfo adInfo) {
if (!isAdBreakActive) {
Log.d(TAG, "No server side ad break active");
return;
}
Log.d(TAG, "Server side ad started");
convivaAdAnalytics.reportAdStarted(convertToConvivaAdInfo(adInfo));
convivaAdAnalytics.reportAdMetric(ConvivaSdkConstants.PLAYBACK.PLAYER_STATE, player.getPlayerState());
}

@Override
public void reportAdFinished() {
if (!isAdBreakActive) {
Log.d(TAG, "No ad break active");
return;
}
Log.d(TAG, "Server side ad finished");
convivaAdAnalytics.reportAdEnded();
}

@Override
public void reportAdSkipped() {
if (!isAdBreakActive) {
Log.d(TAG, "No ad break active");
return;
}
Log.d(TAG, "Server side ad skipped");
convivaAdAnalytics.reportAdSkipped();
}

@Override
public void updateAdInfo(AdInfo adInfo) {
if (!isAdBreakActive) {
Log.d(TAG, "No ad break active");
return;
}
Log.d(TAG, "Setting ad info");

convivaAdAnalytics.setAdInfo(convertToConvivaAdInfo(adInfo));
}

private static Map<String, Object> convertToConvivaAdInfo(AdInfo adInfo) {
HashMap<String, Object> convivaAdInfo = new HashMap<>();
convivaAdInfo.put("c3.ad.id", "NA");
convivaAdInfo.put("c3.ad.system", "NA");
convivaAdInfo.put("c3.ad.mediaFileApiFramework", "NA");
convivaAdInfo.put("c3.ad.firstAdSystem", "NA");
convivaAdInfo.put("c3.ad.firstAdId", "NA");
convivaAdInfo.put("c3.ad.firstCreativeId", "NA");
convivaAdInfo.put("c3.ad.technology", "Server Side");

convivaAdInfo.put("c3.ad.isSlate", adInfo.isSlate());

if (adInfo.getTitle() != null) {
convivaAdInfo.put(ConvivaSdkConstants.ASSET_NAME, adInfo.getTitle());
}
if (adInfo.getDuration() != 0) {
convivaAdInfo.put(ConvivaSdkConstants.DURATION, adInfo.getDuration());
}
if (adInfo.getId() != null) {
convivaAdInfo.put("c3.ad.id", adInfo.getId());
}
if (adInfo.getAdSystem() != null) {
convivaAdInfo.put("c3.ad.system", adInfo.getAdSystem());
}
if (adInfo.getPosition() != null) {
convivaAdInfo.put("c3.ad.position", toConvivaAdPosition(adInfo.getPosition()));
}
if (adInfo.getAdStitcher() != null) {
convivaAdInfo.put("c3.ad.stitcher", adInfo.getAdStitcher());
}
if (adInfo.getAdditionalMetadata() != null) {
convivaAdInfo.putAll(adInfo.getAdditionalMetadata());
}

return convivaAdInfo;
}

private static ConvivaSdkConstants.AdPosition toConvivaAdPosition(AdPosition position) {
switch (position) {
case PREROLL:
return ConvivaSdkConstants.AdPosition.PREROLL;
case POSTROLL:
return ConvivaSdkConstants.AdPosition.POSTROLL;
default:
return ConvivaSdkConstants.AdPosition.MIDROLL;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.bitmovin.analytics.conviva.ssai;

import com.conviva.sdk.ConvivaSdkConstants;

public interface PlaybackStateProvider {
ConvivaSdkConstants.PlayerState getPlayerState();
}
170 changes: 170 additions & 0 deletions conviva/src/main/java/com/bitmovin/analytics/conviva/ssai/SsaiApi.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
package com.bitmovin.analytics.conviva.ssai;

import java.util.Map;

/**
* Enables reporting of server-side ad breaks and ads.
*/
public interface SsaiApi {
/**
* Reports if a server-side ad break is currently active.
*
* @return <code>true</code> if a server-side ad break is active, <code>false</code> otherwise.
*/
boolean isAdBreakActive();

/**
* Reports the start of a server-side ad break. Must be called before the first ad starts.
* Has no effect if a server-side ad break is already playing.
*/
void reportAdBreakStarted();

/**
* Reports the start of a server-side ad break. Must be called before the first ad starts.
* Has no effect if a server-side ad break is already playing.
*
* @param adBreakInfo Map containing metadata about the server-side ad break. Can be <code>null</code>.
*/
void reportAdBreakStarted(Map<String, Object> adBreakInfo);

/**
* Reports the end of a server-side ad break. Must be called after the last ad has finished.
* Has no effect if no server-side ad break is currently active.
*/
void reportAdBreakFinished();

/**
* Reports the start of a server-side ad.
* <p>
* Has to be called after calling the <code>reportAdBreakStarted</code> method.
*
* @param adInfo Object containing metadata about the server-side ad.
*/
void reportAdStarted(AdInfo adInfo);

/**
* Reports the end of a server-side ad.
* Has no effect if no server-side ad is currently playing.
*/
void reportAdFinished();

/**
* Reports that the current ad was skipped.
* Has no effect if no server-side ad is playing.
*/
void reportAdSkipped();

/**
* Updates the ad metadata during an active client-side or server-side ad.
* Has no effect if no server-side ad is playing.
*
* @param adInfo Object containing metadata about the ad.
*/
void updateAdInfo(AdInfo adInfo);

/**
* Represents metadata for an ad.
*/
class AdInfo {
private String title;
private double duration;
private String id;
private String adSystem;
private AdPosition position;
private boolean isSlate = false;
private String adStitcher;
private Map<String, Object> additionalMetadata;

public double getDuration() {
return duration;
}

/**
* Duration of the ad, in seconds.
*/
public void setDuration(double duration) {
this.duration = duration;
}

public String getId() {
return id;
}

/**
* The ad ID extracted from the ad server that contains the ad creative.
*/
public void setId(String id) {
this.id = id;
}

public String getAdSystem() {
return adSystem;
}

/**
* The name of the ad system (i.e., the ad server).
*/
public void setAdSystem(String adSystem) {
this.adSystem = adSystem;
}

public AdPosition getPosition() {
return position;
}

/**
* The position of the ad.
*/
public void setPosition(AdPosition position) {
this.position = position;
}

public boolean isSlate() {
return isSlate;
}

/**
* Indicates whether this ad is a slate or not. Set to <code>true</code> for slate and <code>false</code> for a regular ad.
* Default is <code>false</code>.
*/
public void setSlate(boolean slate) {
isSlate = slate;
}

public String getAdStitcher() {
return adStitcher;
}

/**
* The name of the ad stitcher.
*/
public void setAdStitcher(String adStitcher) {
this.adStitcher = adStitcher;
}

public Map<String, Object> getAdditionalMetadata() {
return additionalMetadata;
}

/**
* Additional ad metadata. This is a map of key-value pairs that can be used to pass additional metadata about the ad.
* A list of ad metadata can be found here: <a href="https://pulse.conviva.com/learning-center/content/sensor_developer_center/sensor_integration/android/android_stream_sensor.htm#IntegrateAdManagers">Conviva documentation</a>
* <p>
* Metadata provided here will supersede any data provided in the ad break info.
*/
public void setAdditionalMetadata(Map<String, Object> additionalMetadata) {
this.additionalMetadata = additionalMetadata;
}

public String getTitle() {
return title;
}

/**
* The title of the ad.
*/
public void setTitle(String title) {
this.title = title;
}
}
}
Loading

0 comments on commit 9d42de0

Please sign in to comment.