Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Chore: rework ad props #4220

Merged
merged 7 commits into from
Oct 10, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.ui.DefaultTimeBar;
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 @@ -781,16 +780,22 @@ private void initializePlayerCore(ReactExoplayerView self) {
.setEnableDecoderFallback(true)
.forceEnableMediaCodecAsynchronousQueueing();

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

// Create an AdsLoader.
adsLoader = new ImaAdsLoader
.Builder(themedReactContext)
.setImaSdkSettings(imaSdkSettings)
.setAdEventListener(this)
.setAdErrorListener(this)
.build();
// 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();
}
DefaultMediaSourceFactory mediaSourceFactory = new DefaultMediaSourceFactory(mediaDataSourceFactory);
if (useCache) {
mediaSourceFactory.setDataSourceFactory(RNVSimpleCache.INSTANCE.getCacheFactory(buildHttpDataSourceFactory(true)));
Expand Down Expand Up @@ -864,6 +869,10 @@ private void initializePlayerSource() {
ArrayList<MediaSource> mediaSourceList = buildTextSources();
MediaSource videoSource = buildMediaSource(source.getUri(), source.getExtension(), drmSessionManager, source.getCropStartMs(), source.getCropEndMs());
MediaSource mediaSourceWithAds = null;
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);
Expand Down Expand Up @@ -1068,11 +1077,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 @@ -1228,8 +1239,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 @@ -1891,14 +1902,6 @@ public void setReportBandwidth(boolean reportBandwidth) {
mReportBandwidth = reportBandwidth;
}

public void setAdTagUrl(final Uri 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 @@ -97,26 +93,6 @@ class ReactExoplayerViewManager(private val config: ReactExoplayerConfig) : View
}
}

@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 @@ -856,6 +859,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
2 changes: 1 addition & 1 deletion examples/basic/src/VideoPlayer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ const VideoPlayer: FC<Props> = ({}) => {
showNotificationControls={showNotificationControls}
ref={videoRef}
source={currentSrc as ReactVideoSource}
adTagUrl={additional?.adTagUrl}
//adTagUrl={additional?.adTagUrl}
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
Loading