diff --git a/src/prebid.js b/src/prebid.js
index 94ade8e5f83..5c5f66d6028 100644
--- a/src/prebid.js
+++ b/src/prebid.js
@@ -52,6 +52,7 @@ import {newMetrics, useMetrics} from './utils/perfMetrics.js';
import {defer, GreedyPromise} from './utils/promise.js';
import {enrichFPD} from './fpd/enrichment.js';
import {allConsent} from './consentHandler.js';
+import {fillVideoDefaults} from './video.js';
const pbjsInstance = getGlobal();
const { triggerUserSyncs } = userSync;
@@ -269,6 +270,12 @@ export const checkAdUnitSetup = hook('sync', function (adUnits) {
return validatedAdUnits;
}, 'checkAdUnitSetup');
+function fillAdUnitDefaults(adUnits) {
+ if (FEATURES.VIDEO) {
+ adUnits.forEach(au => fillVideoDefaults(au))
+ }
+}
+
/// ///////////////////////////////
// //
// Start Public APIs //
@@ -658,6 +665,7 @@ pbjsInstance.requestBids = (function() {
export const startAuction = hook('async', function ({ bidsBackHandler, timeout: cbTimeout, adUnits, ttlBuffer, adUnitCodes, labels, auctionId, ortb2Fragments, metrics, defer } = {}) {
const s2sBidders = getS2SBidderSet(config.getConfig('s2sConfig') || []);
+ fillAdUnitDefaults(adUnits);
adUnits = useMetrics(metrics).measureTime('requestBids.validate', () => checkAdUnitSetup(adUnits));
function auctionDone(bids, timedOut, auctionId) {
diff --git a/src/video.js b/src/video.js
index 7930e318874..90539306011 100644
--- a/src/video.js
+++ b/src/video.js
@@ -1,25 +1,21 @@
-import adapterManager from './adapterManager.js';
-import { deepAccess, logError } from './utils.js';
-import { config } from '../src/config.js';
-import {includes} from './polyfill.js';
-import { hook } from './hook.js';
+import {deepAccess, logError} from './utils.js';
+import {config} from '../src/config.js';
+import {hook} from './hook.js';
import {auctionManager} from './auctionManager.js';
-const VIDEO_MEDIA_TYPE = 'video';
export const OUTSTREAM = 'outstream';
export const INSTREAM = 'instream';
-/**
- * Helper functions for working with video-enabled adUnits
- */
-export const videoAdUnit = adUnit => {
- const mediaType = adUnit.mediaType === VIDEO_MEDIA_TYPE;
- const mediaTypes = deepAccess(adUnit, 'mediaTypes.video');
- return mediaType || mediaTypes;
-};
-export const videoBidder = bid => includes(adapterManager.videoAdapters, bid.bidder);
-export const hasNonVideoBidder = adUnit =>
- adUnit.bids.filter(bid => !videoBidder(bid)).length;
+export function fillVideoDefaults(adUnit) {
+ const video = adUnit?.mediaTypes?.video;
+ if (video != null && video.plcmt == null) {
+ if (video.context === OUTSTREAM || [2, 3, 4].includes(video.placement)) {
+ video.plcmt = 4;
+ } else if (video.context !== OUTSTREAM && [2, 6].includes(video.playmethod)) {
+ video.plcmt = 2;
+ }
+ }
+}
/**
* @typedef {object} VideoBid
diff --git a/test/spec/video_spec.js b/test/spec/video_spec.js
index 61621c7ec42..0911f087587 100644
--- a/test/spec/video_spec.js
+++ b/test/spec/video_spec.js
@@ -1,4 +1,4 @@
-import { isValidVideoBid } from 'src/video.js';
+import {fillVideoDefaults, isValidVideoBid} from 'src/video.js';
import {hook} from '../../src/hook.js';
import {stubAuctionIndex} from '../helpers/indexStub.js';
@@ -7,97 +7,169 @@ describe('video.js', function () {
hook.ready();
});
- it('validates valid instream bids', function () {
- const bid = {
- adId: '456xyz',
- vastUrl: 'http://www.example.com/vastUrl',
- transactionId: 'au'
- };
- const adUnits = [{
- transactionId: 'au',
- mediaTypes: {
- video: {context: 'instream'}
- }
- }];
- const valid = isValidVideoBid(bid, {index: stubAuctionIndex({adUnits})});
- expect(valid).to.equal(true);
- });
+ describe('fillVideoDefaults', () => {
+ function fillDefaults(videoMediaType = {}) {
+ const adUnit = {mediaTypes: {video: videoMediaType}};
+ fillVideoDefaults(adUnit);
+ return adUnit.mediaTypes.video;
+ }
- it('catches invalid instream bids', function () {
- const bid = {
- transactionId: 'au'
- };
- const adUnits = [{
- transactionId: 'au',
- mediaTypes: {
- video: {context: 'instream'}
- }
- }];
- const valid = isValidVideoBid(bid, {index: stubAuctionIndex({adUnits})});
- expect(valid).to.equal(false);
- });
+ describe('should set plcmt = 4 when', () => {
+ it('context is "outstream"', () => {
+ expect(fillDefaults({context: 'outstream'})).to.eql({
+ context: 'outstream',
+ plcmt: 4
+ })
+ });
+ [2, 3, 4].forEach(placement => {
+ it(`placemement is "${placement}"`, () => {
+ expect(fillDefaults({placement})).to.eql({
+ placement,
+ plcmt: 4
+ });
+ })
+ });
+ });
+ describe('should set plcmt = 2 when', () => {
+ [2, 6].forEach(playmethod => {
+ it(`playmethod is "${playmethod}"`, () => {
+ expect(fillDefaults({playmethod})).to.eql({
+ playmethod,
+ plcmt: 2,
+ });
+ });
+ });
+ });
+ describe('should not set plcmt when', () => {
+ Object.entries({
+ 'it was set by pub (context=outstream)': {
+ expected: 1,
+ video: {
+ context: 'outstream',
+ plcmt: 1
+ }
+ },
+ 'it was set by pub (placement=2)': {
+ expected: 1,
+ video: {
+ placement: 2,
+ plcmt: 1
+ }
+ },
+ 'placement not in 2, 3, 4': {
+ expected: undefined,
+ video: {
+ placement: 1
+ }
+ },
+ 'it was set by pub (playmethod=2)': {
+ expected: 1,
+ video: {
+ plcmt: 1,
+ playmethod: 2
+ }
+ }
+ }).forEach(([t, {expected, video}]) => {
+ it(t, () => {
+ expect(fillDefaults(video).plcmt).to.eql(expected);
+ })
+ })
+ })
+ })
- it('catches invalid bids when prebid-cache is disabled', function () {
- const adUnits = [{
- transactionId: 'au',
- bidder: 'vastOnlyVideoBidder',
- mediaTypes: {video: {}},
- }];
+ describe('isValidVideoBid', () => {
+ it('validates valid instream bids', function () {
+ const bid = {
+ adId: '456xyz',
+ vastUrl: 'http://www.example.com/vastUrl',
+ transactionId: 'au'
+ };
+ const adUnits = [{
+ transactionId: 'au',
+ mediaTypes: {
+ video: {context: 'instream'}
+ }
+ }];
+ const valid = isValidVideoBid(bid, {index: stubAuctionIndex({adUnits})});
+ expect(valid).to.equal(true);
+ });
- const valid = isValidVideoBid({ transactionId: 'au', vastXml: 'vast' }, {index: stubAuctionIndex({adUnits})});
+ it('catches invalid instream bids', function () {
+ const bid = {
+ transactionId: 'au'
+ };
+ const adUnits = [{
+ transactionId: 'au',
+ mediaTypes: {
+ video: {context: 'instream'}
+ }
+ }];
+ const valid = isValidVideoBid(bid, {index: stubAuctionIndex({adUnits})});
+ expect(valid).to.equal(false);
+ });
- expect(valid).to.equal(false);
- });
+ it('catches invalid bids when prebid-cache is disabled', function () {
+ const adUnits = [{
+ transactionId: 'au',
+ bidder: 'vastOnlyVideoBidder',
+ mediaTypes: {video: {}},
+ }];
- it('validates valid outstream bids', function () {
- const bid = {
- transactionId: 'au',
- renderer: {
- url: 'render.url',
- render: () => true,
- }
- };
- const adUnits = [{
- transactionId: 'au',
- mediaTypes: {
- video: {context: 'outstream'}
- }
- }];
- const valid = isValidVideoBid(bid, {index: stubAuctionIndex({adUnits})});
- expect(valid).to.equal(true);
- });
+ const valid = isValidVideoBid({ transactionId: 'au', vastXml: 'vast' }, {index: stubAuctionIndex({adUnits})});
- it('validates valid outstream bids with a publisher defined renderer', function () {
- const bid = {
- transactionId: 'au',
- };
- const adUnits = [{
- transactionId: 'au',
- mediaTypes: {
- video: {
- context: 'outstream',
+ expect(valid).to.equal(false);
+ });
+
+ it('validates valid outstream bids', function () {
+ const bid = {
+ transactionId: 'au',
+ renderer: {
+ url: 'render.url',
+ render: () => true,
}
- },
- renderer: {
- url: 'render.url',
- render: () => true,
- }
- }];
- const valid = isValidVideoBid(bid, {index: stubAuctionIndex({adUnits})});
- expect(valid).to.equal(true);
- });
+ };
+ const adUnits = [{
+ transactionId: 'au',
+ mediaTypes: {
+ video: {context: 'outstream'}
+ }
+ }];
+ const valid = isValidVideoBid(bid, {index: stubAuctionIndex({adUnits})});
+ expect(valid).to.equal(true);
+ });
- it('catches invalid outstream bids', function () {
- const bid = {
- transactionId: 'au',
- };
- const adUnits = [{
- transactionId: 'au',
- mediaTypes: {
- video: {context: 'outstream'}
- }
- }];
- const valid = isValidVideoBid(bid, {index: stubAuctionIndex({adUnits})});
- expect(valid).to.equal(false);
- });
+ it('validates valid outstream bids with a publisher defined renderer', function () {
+ const bid = {
+ transactionId: 'au',
+ };
+ const adUnits = [{
+ transactionId: 'au',
+ mediaTypes: {
+ video: {
+ context: 'outstream',
+ }
+ },
+ renderer: {
+ url: 'render.url',
+ render: () => true,
+ }
+ }];
+ const valid = isValidVideoBid(bid, {index: stubAuctionIndex({adUnits})});
+ expect(valid).to.equal(true);
+ });
+
+ it('catches invalid outstream bids', function () {
+ const bid = {
+ transactionId: 'au',
+ };
+ const adUnits = [{
+ transactionId: 'au',
+ mediaTypes: {
+ video: {context: 'outstream'}
+ }
+ }];
+ const valid = isValidVideoBid(bid, {index: stubAuctionIndex({adUnits})});
+ expect(valid).to.equal(false);
+ });
+ })
});