Skip to content

Commit

Permalink
Chore: rework ad props (#4220)
Browse files Browse the repository at this point in the history
* fix: move ad configuration in source
  • Loading branch information
freeboub authored Oct 10, 2024
1 parent 9a3fcda commit d86adc5
Show file tree
Hide file tree
Showing 14 changed files with 171 additions and 76 deletions.
46 changes: 46 additions & 0 deletions android/src/main/java/com/brentvatne/common/api/AdsProps.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.brentvatne.common.api

import android.net.Uri
import android.text.TextUtils
import com.brentvatne.common.toolbox.DebugLog
import com.brentvatne.common.toolbox.ReactBridgeUtils
import com.facebook.react.bridge.ReadableMap

class AdsProps {
var adTagUrl: Uri? = null
var adLanguage: String? = null

/** return true if this and src are equals */
override fun equals(other: Any?): Boolean {
if (other == null || other !is AdsProps) return false
return (
adTagUrl == other.adTagUrl &&
adLanguage == other.adLanguage
)
}

companion object {
private const val PROP_AD_TAG_URL = "adTagUrl"
private const val PROP_AD_LANGUAGE = "adLanguage"

@JvmStatic
fun parse(src: ReadableMap?): AdsProps {
val adsProps = AdsProps()
DebugLog.w("olivier", "uri: parse AdsProps")

if (src != null) {
val uriString = ReactBridgeUtils.safeGetString(src, PROP_AD_TAG_URL)
if (TextUtils.isEmpty(uriString)) {
adsProps.adTagUrl = null
} else {
adsProps.adTagUrl = Uri.parse(uriString)
}
val languageString = ReactBridgeUtils.safeGetString(src, PROP_AD_LANGUAGE)
if (!TextUtils.isEmpty(languageString)) {
adsProps.adLanguage = languageString
}
}
return adsProps
}
}
}
13 changes: 12 additions & 1 deletion android/src/main/java/com/brentvatne/common/api/Source.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import com.brentvatne.common.toolbox.ReactBridgeUtils.safeGetBool
import com.brentvatne.common.toolbox.ReactBridgeUtils.safeGetInt
import com.brentvatne.common.toolbox.ReactBridgeUtils.safeGetMap
import com.brentvatne.common.toolbox.ReactBridgeUtils.safeGetString
import com.brentvatne.react.BuildConfig
import com.facebook.react.bridge.ReadableMap
import java.util.Locale
import java.util.Objects
Expand Down Expand Up @@ -65,6 +66,11 @@ class Source {
*/
var cmcdProps: CMCDProps? = null

/**
* Ads playback properties
*/
var adsProps: AdsProps? = null

/**
* The list of sideLoaded text tracks
*/
Expand All @@ -84,7 +90,8 @@ class Source {
drmProps == other.drmProps &&
contentStartTime == other.contentStartTime &&
cmcdProps == other.cmcdProps &&
sideLoadedTextTracks == other.sideLoadedTextTracks
sideLoadedTextTracks == other.sideLoadedTextTracks &&
adsProps == other.adsProps
)
}

Expand Down Expand Up @@ -149,6 +156,7 @@ class Source {
private const val PROP_SRC_HEADERS = "requestHeaders"
private const val PROP_SRC_DRM = "drm"
private const val PROP_SRC_CMCD = "cmcd"
private const val PROP_SRC_ADS = "ad"
private const val PROP_SRC_TEXT_TRACKS_ALLOW_CHUNKLESS_PREPARATION = "textTracksAllowChunklessPreparation"
private const val PROP_SRC_TEXT_TRACKS = "textTracks"

Expand Down Expand Up @@ -210,6 +218,9 @@ class Source {
source.extension = safeGetString(src, PROP_SRC_TYPE, null)
source.drmProps = parse(safeGetMap(src, PROP_SRC_DRM))
source.cmcdProps = CMCDProps.parse(safeGetMap(src, PROP_SRC_CMCD))
if (BuildConfig.USE_EXOPLAYER_IMA) {
source.adsProps = AdsProps.parse(safeGetMap(src, PROP_SRC_ADS))
}
source.textTracksAllowChunklessPreparation = safeGetBool(src, PROP_SRC_TEXT_TRACKS_ALLOW_CHUNKLESS_PREPARATION, true)
source.sideLoadedTextTracks = SideLoadedTextTrackList.parse(safeGetArray(src, PROP_SRC_TEXT_TRACKS))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@
import androidx.media3.session.MediaSessionService;
import androidx.media3.ui.LegacyPlayerControlView;

import com.brentvatne.common.api.AdsProps;
import com.brentvatne.common.api.BufferConfig;
import com.brentvatne.common.api.BufferingStrategy;
import com.brentvatne.common.api.ControlsConfig;
Expand Down Expand Up @@ -240,8 +241,6 @@ public class ReactExoplayerView extends FrameLayout implements
private boolean playInBackground = false;
private boolean mReportBandwidth = false;
private boolean controls;
private Uri adTagUrl;
private String adLanguage;

private boolean showNotificationControls = false;
// \ End props
Expand Down Expand Up @@ -827,16 +826,23 @@ private void initializePlayerCore(ReactExoplayerView self) {
mediaSourceFactory.setDataSourceFactory(RNVSimpleCache.INSTANCE.getCacheFactory(buildHttpDataSourceFactory(true)));
}

ImaSdkSettings imaSdkSettings = ImaSdkFactory.getInstance().createImaSdkSettings();
imaSdkSettings.setLanguage(adLanguage);
if (BuildConfig.USE_EXOPLAYER_IMA) {
AdsProps adProps = source.getAdsProps();

// Create an AdsLoader.
ImaAdsLoader.Builder imaLoaderBuilder = new ImaAdsLoader
.Builder(themedReactContext)
.setAdEventListener(this)
.setAdErrorListener(this);

if (adProps != null && adProps.getAdLanguage() != null) {
ImaSdkSettings imaSdkSettings = ImaSdkFactory.getInstance().createImaSdkSettings();
imaSdkSettings.setLanguage(adProps.getAdLanguage());
imaLoaderBuilder.setImaSdkSettings(imaSdkSettings);
}
adsLoader = imaLoaderBuilder.build();
}

// Create an AdsLoader.
adsLoader = new ImaAdsLoader
.Builder(themedReactContext)
.setImaSdkSettings(imaSdkSettings)
.setAdEventListener(this)
.setAdErrorListener(this)
.build();
mediaSourceFactory.setLocalAdInsertionComponents(unusedAdTagUri -> adsLoader, exoPlayerView);

player = new ExoPlayer.Builder(getContext(), renderersFactory)
Expand Down Expand Up @@ -904,7 +910,11 @@ private void initializePlayerSource(Source runningSource) {
ArrayList<MediaSource> mediaSourceList = buildTextSources();
MediaSource videoSource = buildMediaSource(runningSource.getUri(), runningSource.getExtension(), drmSessionManager, runningSource.getCropStartMs(), runningSource.getCropEndMs());
MediaSource mediaSourceWithAds = null;
if (adTagUrl != null && BuildConfig.USE_EXOPLAYER_IMA) {
Uri adTagUrl = null;
if (source.getAdsProps() != null) {
adTagUrl = source.getAdsProps().getAdTagUrl();
}
if (adTagUrl != null && adsLoader != null) {
DefaultMediaSourceFactory mediaSourceFactory = new DefaultMediaSourceFactory(mediaDataSourceFactory)
.setLocalAdInsertionComponents(unusedAdTagUri -> adsLoader, exoPlayerView);
DataSpec adTagDataSpec = new DataSpec(adTagUrl);
Expand Down Expand Up @@ -1105,11 +1115,13 @@ private MediaSource buildMediaSource(Uri uri, String overrideExtension, DrmSessi
if (customMetadata != null) {
mediaItemBuilder.setMediaMetadata(customMetadata);
}

if (adTagUrl != null) {
mediaItemBuilder.setAdsConfiguration(
new MediaItem.AdsConfiguration.Builder(adTagUrl).build()
);
if (source.getAdsProps() != null) {
Uri adTagUrl = source.getAdsProps().getAdTagUrl();
if (adTagUrl != null) {
mediaItemBuilder.setAdsConfiguration(
new MediaItem.AdsConfiguration.Builder(adTagUrl).build()
);
}
}

MediaItem.LiveConfiguration.Builder liveConfiguration = ConfigurationUtils.getLiveConfiguration(bufferConfig);
Expand Down Expand Up @@ -1261,8 +1273,8 @@ private void releasePlayer() {

if (adsLoader != null) {
adsLoader.release();
adsLoader = null;
}
adsLoader = null;
progressHandler.removeMessages(SHOW_PROGRESS);
audioBecomingNoisyReceiver.removeListener();
bandwidthMeter.removeEventListener(this);
Expand Down Expand Up @@ -1926,15 +1938,6 @@ public void setReportBandwidth(boolean reportBandwidth) {
mReportBandwidth = reportBandwidth;
}

public void setAdTagUrl(final Uri uri) {
DebugLog.w(TAG, "setAdTagUrl" + uri);
adTagUrl = uri;
}

public void setAdLanguage(final String language) {
adLanguage = language;
}

private void reloadSource() {
playerNeedsSource = true;
initializePlayer();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package com.brentvatne.exoplayer

import android.graphics.Color
import android.net.Uri
import android.text.TextUtils
import android.util.Log
import com.brentvatne.common.api.BufferConfig
import com.brentvatne.common.api.BufferingStrategy
Expand All @@ -26,8 +24,6 @@ class ReactExoplayerViewManager(private val config: ReactExoplayerConfig) : View
private const val TAG = "ExoViewManager"
private const val REACT_CLASS = "RCTVideo"
private const val PROP_SRC = "src"
private const val PROP_AD_TAG_URL = "adTagUrl"
private const val PROP_AD_LANGUAGE = "adLanguage"
private const val PROP_RESIZE_MODE = "resizeMode"
private const val PROP_REPEAT = "repeat"
private const val PROP_SELECTED_AUDIO_TRACK = "selectedAudioTrack"
Expand Down Expand Up @@ -92,26 +88,6 @@ class ReactExoplayerViewManager(private val config: ReactExoplayerConfig) : View
videoView.setSrc(Source.parse(src, context))
}

@ReactProp(name = PROP_AD_TAG_URL)
fun setAdTagUrl(videoView: ReactExoplayerView, uriString: String?) {
if (TextUtils.isEmpty(uriString)) {
videoView.setAdTagUrl(null)
return
}
val adTagUrl = Uri.parse(uriString)
videoView.setAdTagUrl(adTagUrl)
}

@ReactProp(name = PROP_AD_LANGUAGE)
fun setAdLanguage(videoView: ReactExoplayerView, languageString: String?) {
if (TextUtils.isEmpty(languageString)) {
videoView.setAdLanguage(null) // Maybe "en" default?
return
}

videoView.setAdLanguage(languageString)
}

@ReactProp(name = PROP_RESIZE_MODE)
fun setResizeMode(videoView: ReactExoplayerView, resizeMode: String) {
when (resizeMode) {
Expand Down
23 changes: 23 additions & 0 deletions docs/pages/component/props.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ This page shows the list of available properties to configure player

### `adTagUrl`

> [!WARNING]
> Deprecated, use source.ad.adTagUrl instead
<PlatformsList types={['Android', 'iOS']} />

Sets the VAST uri to play AVOD ads.
Expand Down Expand Up @@ -858,6 +861,26 @@ source={{
}}
```

### `ad`

<PlatformsList types={['Android', 'iOS']} />

Sets the ad configuration.

Example:

```
ad: {
adTagUrl="https://pubads.g.doubleclick.net/gampad/ads?iu=/21775744923/external/vmap_ad_samples&sz=640x480&cust_params=sample_ar%3Dpremidpostoptimizedpodbumper&ciu_szs=300x250&gdfp_req=1&ad_rule=1&output=vmap&unviewed_position_start=1&env=vp&impl=s&cmsid=496&vid=short_onecue&correlator="
adLanguage="fr"
}
```

See: [./ads.md] for more informations

Note: You need enable IMA SDK in gradle or pod file - [enable client side ads insertion](/installation)


#### `contentStartTime`

<PlatformsList types={['Android']} />
Expand Down
3 changes: 1 addition & 2 deletions examples/basic/src/VideoPlayer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -265,8 +265,7 @@ const VideoPlayer: FC<Props> = ({}) => {
<Video
showNotificationControls={showNotificationControls}
ref={videoRef}
// source={currentSrc as ReactVideoSource}
adTagUrl={additional?.adTagUrl}
// source={currentSrc as ReactVideoSource}
drm={additional?.drm}
style={viewStyle}
rate={rate}
Expand Down
14 changes: 8 additions & 6 deletions examples/basic/src/constants/general.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,14 @@ export const srcAllPlatformList = [
},
],
},
{
description: '(mp4) big buck bunny With Ads',
ad: {
adTagUrl:
'https://pubads.g.doubleclick.net/gampad/ads?iu=/21775744923/external/vmap_ad_samples&sz=640x480&cust_params=sample_ar%3Dpremidpostoptimizedpodbumper&ciu_szs=300x250&gdfp_req=1&ad_rule=1&output=vmap&unviewed_position_start=1&env=vp&impl=s&cmsid=496&vid=short_onecue&correlator=',
},
uri: 'https://d23dyxeqlo5psv.cloudfront.net/big_buck_bunny.mp4',
},
];

export const srcIosList: SampleVideoSource[] = [];
Expand All @@ -133,12 +141,6 @@ export const srcAndroidList: SampleVideoSource[] = [
uri: 'http://www.youtube.com/api/manifest/dash/id/bf5bb2419360daf1/source/youtube?as=fmp4_audio_clear,fmp4_sd_hd_clear&sparams=ip,ipbits,expire,source,id,as&ip=0.0.0.0&ipbits=0&expire=19000000000&signature=51AF5F39AB0CEC3E5497CD9C900EBFEAECCCB5C7.8506521BFC350652163895D4C26DEE124209AA9E&key=ik0',
type: 'mpd',
},
{
description: '(mp4) big buck bunny With Ads',
adTagUrl:
'https://pubads.g.doubleclick.net/gampad/ads?iu=/21775744923/external/vmap_ad_samples&sz=640x480&cust_params=sample_ar%3Dpremidpostoptimizedpodbumper&ciu_szs=300x250&gdfp_req=1&ad_rule=1&output=vmap&unviewed_position_start=1&env=vp&impl=s&cmsid=496&vid=short_onecue&correlator=',
uri: 'http://d23dyxeqlo5psv.cloudfront.net/big_buck_bunny.mp4',
},
{
description: 'WV: Secure SD & HD (cbcs,MP4,H264)',
uri: 'https://storage.googleapis.com/wvmedia/cbcs/h264/tears/tears_aes_cbcs.mpd',
Expand Down
18 changes: 18 additions & 0 deletions ios/Video/DataStructures/AdParams.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
struct AdParams {
let adTagUrl: String?
let adLanguage: String?

let json: NSDictionary?

init(_ json: NSDictionary!) {
guard json != nil else {
self.json = nil
adTagUrl = nil
adLanguage = nil
return
}
self.json = json
adTagUrl = json["adTagUrl"] as? String
adLanguage = json["adLanguage"] as? String
}
}
3 changes: 3 additions & 0 deletions ios/Video/DataStructures/VideoSource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ struct VideoSource {
/* DRM */
let drm: DRMParams
var textTracks: [TextTrack] = []
let adParams: AdParams

let json: NSDictionary?

Expand All @@ -29,6 +30,7 @@ struct VideoSource {
self.cropEnd = nil
self.customMetadata = nil
self.drm = DRMParams(nil)
adParams = AdParams(nil)
return
}
self.json = json
Expand Down Expand Up @@ -56,5 +58,6 @@ struct VideoSource {
self.textTracks = (json["textTracks"] as? NSArray)?.map { trackDict in
return TextTrack(trackDict as? NSDictionary)
} ?? []
adParams = AdParams(json["ad"] as? NSDictionary)
}
}
Loading

0 comments on commit d86adc5

Please sign in to comment.