├── .gitignore
├── LICENSE
├── README.md
├── advanced
├── ads.js
├── application.js
├── index.html
├── style.css
└── video_player.js
├── attempt_to_autoplay
├── ads.js
├── index.html
└── style.css
├── audio_in_video
├── ads.js
├── image.png
├── index.html
└── style.css
├── live_stream_prefetch
├── ads.js
├── index.html
└── style.css
├── mobile_auto_skippable
├── ads.js
├── index.html
└── style.css
├── playlist
├── ads.js
├── application.js
├── img
│ ├── android_preview.jpg
│ └── doubleclick_preview.jpg
├── index.html
├── style.css
└── video_player.js
├── simple
├── ads.js
├── index.html
└── style.css
└── vpaid
├── linear
└── VpaidVideoAd.js
└── nonlinear
└── VpaidNonLinear.js
/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/googleads/googleads-ima-html5/bc910dc76a00f46b81483c81ee057da696933799/.gitignore
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Google Ads HTML5 IMA SDK
2 |
3 | This project hosts samples for the
4 | [HTML5 IMA SDK](//developers.google.com/interactive-media-ads/docs/sdks/html5/client-side).
5 |
6 | ### Samples Breakdown
7 |
8 | * [Simple](https://github.com/googleads/googleads-ima-html5/tree/main/simple) -
9 | the bare minimum required for an IMA integration
10 | * [Advanced](https://github.com/googleads/googleads-ima-html5/tree/main/advanced) -
11 | IMA integration with more advanced UI including event logging, play/pause,
12 | and fullscreen, and companion ads
13 | * [Playlist](https://github.com/googleads/googleads-ima-html5/tree/main/playlist) -
14 | expands on the advanced sample to demonstrate an integration with a video
15 | playlist
16 | * [Audio in video](https://github.com/googleads/googleads-ima-html5/tree/main/audio_in_video) -
17 | demonstrates the use of the
18 | [poster image feature](https://developers.google.com/interactive-media-ads/docs/sdks/html5/client-side/audio-poster)
19 | in an app that supports either audio or video ads.
20 | * [VPAID](https://github.com/googleads/googleads-ima-html5/tree/main/vpaid) -
21 | these JavaScript samples are used in the
22 | [IMA VPAID sample ad tags](https://developers.google.com/interactive-media-ads/docs/sdks/html5/client-side/tags#single-vpaid-2.0-linear).
23 | The code in these samples is needed in VPAID ads, but not in IMA
24 | implementations for VPAID ads. For more information on VPAID ads, see the
25 | [IAB VPAID docs](https://iabtechlab.com/standards/video-player-ad-interface-definition-vpaid/).
26 |
27 | ### Requirements
28 |
29 | * Your favorite text editor
30 | * An HTML5 compliant browser
31 | * A webserver on which to host the sample
32 |
33 | ### Downloads
34 |
35 | Check out the
36 | [releases section](https://github.com/googleads/googleads-ima-html5/releases)
37 | for downloadable zips of the source.
38 |
39 | ### More Info
40 |
41 | For more information, see the documentation at
42 | https://developers.google.com/interactive-media-ads/docs/sdks/html5/v3/.
43 |
44 | ### Announcements and Updates
45 |
46 | For API and client library updates and news, follow our
47 | [Google Ads Developers blog](http://googleadsdeveloper.blogspot.com/)
48 |
49 | Copyright 2013 Google Inc. All Rights Reserved. You may study, modify, and use
50 | this example for any purpose. Note that this example is provided "as is",
51 | WITHOUT WARRANTY of any kind either expressed or implied.
52 |
--------------------------------------------------------------------------------
/advanced/ads.js:
--------------------------------------------------------------------------------
1 | // Copyright 2013 Google Inc. All Rights Reserved.
2 | // You may study, modify, and use this example for any purpose.
3 | // Note that this example is provided "as is", WITHOUT WARRANTY
4 | // of any kind either expressed or implied.
5 |
6 | /**
7 | * Shows how to use the IMA SDK to request and display ads.
8 | */
9 | const Ads = function(application, videoPlayer) {
10 | this.application_ = application;
11 | this.videoPlayer_ = videoPlayer;
12 | this.customClickDiv_ = document.getElementById('customClick');
13 | this.contentCompleteCalled_ = false;
14 | google.ima.settings.setVpaidMode(google.ima.ImaSdkSettings.VpaidMode.ENABLED);
15 | // Call setLocale() to localize language text and downloaded swfs
16 | // google.ima.settings.setLocale('fr');
17 | this.adDisplayContainer_ = new google.ima.AdDisplayContainer(
18 | this.videoPlayer_.adContainer, this.videoPlayer_.contentPlayer,
19 | this.customClickDiv_);
20 | this.adsLoader_ = new google.ima.AdsLoader(this.adDisplayContainer_);
21 | this.adsManager_ = null;
22 |
23 | this.adsLoader_.addEventListener(
24 | google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED,
25 | this.onAdsManagerLoaded_, false, this);
26 | this.adsLoader_.addEventListener(
27 | google.ima.AdErrorEvent.Type.AD_ERROR, this.onAdError_, false, this);
28 | };
29 |
30 | // On iOS and Android devices, video playback must begin in a user action.
31 | // AdDisplayContainer provides a initialize() API to be called at appropriate
32 | // time.
33 | // This should be called when the user clicks or taps.
34 | Ads.prototype.initialUserAction = function() {
35 | this.adDisplayContainer_.initialize();
36 | this.videoPlayer_.contentPlayer.load();
37 | };
38 |
39 | Ads.prototype.requestAds = function(adTagUrl) {
40 | const adsRequest = new google.ima.AdsRequest();
41 | adsRequest.adTagUrl = adTagUrl;
42 | adsRequest.linearAdSlotWidth = this.videoPlayer_.width;
43 | adsRequest.linearAdSlotHeight = this.videoPlayer_.height;
44 | adsRequest.nonLinearAdSlotWidth = this.videoPlayer_.width;
45 | adsRequest.nonLinearAdSlotHeight = this.videoPlayer_.height;
46 | this.adsLoader_.requestAds(adsRequest);
47 | };
48 |
49 | Ads.prototype.pause = function() {
50 | if (this.adsManager_) {
51 | this.adsManager_.pause();
52 | }
53 | };
54 |
55 | Ads.prototype.resume = function() {
56 | if (this.adsManager_) {
57 | this.adsManager_.resume();
58 | }
59 | };
60 |
61 | Ads.prototype.resize = function(width, height) {
62 | if (this.adsManager_) {
63 | this.adsManager_.resize(width, height);
64 | }
65 | };
66 |
67 | Ads.prototype.contentEnded = function() {
68 | this.contentCompleteCalled_ = true;
69 | this.adsLoader_.contentComplete();
70 | };
71 |
72 | Ads.prototype.onAdsManagerLoaded_ = function(adsManagerLoadedEvent) {
73 | this.application_.log('Ads loaded.');
74 | const adsRenderingSettings = new google.ima.AdsRenderingSettings();
75 | adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true;
76 | this.adsManager_ = adsManagerLoadedEvent.getAdsManager(
77 | this.videoPlayer_.contentPlayer, adsRenderingSettings);
78 | this.startAdsManager_(this.adsManager_);
79 | };
80 |
81 | Ads.prototype.startAdsManager_ = function(adsManager) {
82 | if (adsManager.isCustomClickTrackingUsed()) {
83 | this.customClickDiv_.style.display = 'table';
84 | }
85 | // Attach the pause/resume events.
86 | adsManager.addEventListener(
87 | google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED,
88 | this.onContentPauseRequested_, false, this);
89 | adsManager.addEventListener(
90 | google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED,
91 | this.onContentResumeRequested_, false, this);
92 | // Handle errors.
93 | adsManager.addEventListener(
94 | google.ima.AdErrorEvent.Type.AD_ERROR, this.onAdError_, false, this);
95 | const events = [
96 | google.ima.AdEvent.Type.ALL_ADS_COMPLETED, google.ima.AdEvent.Type.CLICK,
97 | google.ima.AdEvent.Type.COMPLETE, google.ima.AdEvent.Type.FIRST_QUARTILE,
98 | google.ima.AdEvent.Type.LOADED, google.ima.AdEvent.Type.MIDPOINT,
99 | google.ima.AdEvent.Type.PAUSED, google.ima.AdEvent.Type.STARTED,
100 | google.ima.AdEvent.Type.THIRD_QUARTILE
101 | ];
102 | for (const index in events) {
103 | adsManager.addEventListener(events[index], this.onAdEvent_, false, this);
104 | }
105 |
106 | let initWidth;
107 | let initHeight;
108 | if (this.application_.fullscreen) {
109 | initWidth = this.application_.fullscreenWidth;
110 | initHeight = this.application_.fullscreenHeight;
111 | } else {
112 | initWidth = this.videoPlayer_.width;
113 | initHeight = this.videoPlayer_.height;
114 | }
115 | adsManager.init(initWidth, initHeight);
116 |
117 | adsManager.start();
118 | };
119 |
120 | Ads.prototype.onContentPauseRequested_ = function() {
121 | this.application_.pauseForAd();
122 | this.application_.setVideoEndedCallbackEnabled(false);
123 | };
124 |
125 | Ads.prototype.onContentResumeRequested_ = function() {
126 | this.application_.setVideoEndedCallbackEnabled(true);
127 | // Without this check the video starts over from the beginning on a
128 | // post-roll's CONTENT_RESUME_REQUESTED
129 | if (!this.contentCompleteCalled_) {
130 | this.application_.resumeAfterAd();
131 | }
132 | };
133 |
134 | Ads.prototype.onAdEvent_ = function(adEvent) {
135 | this.application_.log('Ad event: ' + adEvent.type);
136 |
137 | if (adEvent.type == google.ima.AdEvent.Type.CLICK) {
138 | this.application_.adClicked();
139 | } else if (adEvent.type == google.ima.AdEvent.Type.LOADED) {
140 | const ad = adEvent.getAd();
141 | if (!ad.isLinear()) {
142 | this.onContentResumeRequested_();
143 | }
144 | }
145 | };
146 |
147 | Ads.prototype.onAdError_ = function(adErrorEvent) {
148 | this.application_.log('Ad error: ' + adErrorEvent.getError().toString());
149 | if (this.adsManager_) {
150 | this.adsManager_.destroy();
151 | }
152 | this.application_.resumeAfterAd();
153 | };
154 |
--------------------------------------------------------------------------------
/advanced/application.js:
--------------------------------------------------------------------------------
1 | // Copyright 2013 Google Inc. All Rights Reserved.
2 | // You may study, modify, and use this example for any purpose.
3 | // Note that this example is provided "as is", WITHOUT WARRANTY
4 | // of any kind either expressed or implied.
5 |
6 | /**
7 | * Handles user interaction and creates the player and ads controllers.
8 | */
9 | const Application = function() {
10 | this.adTagBox_ = document.getElementById('tagText');
11 | this.sampleAdTag_ = document.getElementById('sampleAdTag');
12 | this.sampleAdTag_.addEventListener(
13 | 'click', this.bind_(this, this.onSampleAdTagClick_), false);
14 | this.console_ = document.getElementById('console');
15 | this.playButton_ = document.getElementById('playpause');
16 | this.playButton_.addEventListener(
17 | 'click', this.bind_(this, this.onClick_), false);
18 | this.fullscreenButton_ = document.getElementById('fullscreen');
19 | this.fullscreenButton_.addEventListener(
20 | 'click', this.bind_(this, this.onFullscreenClick_), false);
21 |
22 | this.fullscreenWidth = null;
23 | this.fullscreenHeight = null;
24 |
25 | const fullScreenEvents =
26 | ['fullscreenchange', 'mozfullscreenchange', 'webkitfullscreenchange'];
27 | for (let key in fullScreenEvents) {
28 | document.addEventListener(
29 | fullScreenEvents[key], this.bind_(this, this.onFullscreenChange_),
30 | false);
31 | }
32 |
33 | this.playing_ = false;
34 | this.adsActive_ = false;
35 | this.adsDone_ = false;
36 | this.fullscreen = false;
37 |
38 | this.videoPlayer_ = new VideoPlayer();
39 | this.ads_ = new Ads(this, this.videoPlayer_);
40 | this.adTagUrl_ = '';
41 |
42 | this.videoEndedCallback_ = this.bind_(this, this.onContentEnded_);
43 | this.setVideoEndedCallbackEnabled(true);
44 | };
45 |
46 | Application.prototype.SAMPLE_AD_TAG_ = 'https://pubads.g.doubleclick.net/' +
47 | 'gampad/ads?iu=/21775744923/external/single_ad_samples&sz=640x480&' +
48 | 'cust_params=sample_ct%3Dlinear&ciu_szs=300x250%2C728x90&' +
49 | 'gdfp_req=1&output=vast&unviewed_position_start=1&env=vp&impl=s&' +
50 | 'correlator=';
51 |
52 | /**
53 | * Registers or removes video ended callback based on the 'enable' param.
54 | * @param {boolean} enable
55 | */
56 | Application.prototype.setVideoEndedCallbackEnabled = function(enable) {
57 | if (enable) {
58 | this.videoPlayer_.registerVideoEndedCallback(this.videoEndedCallback_);
59 | } else {
60 | this.videoPlayer_.removeVideoEndedCallback(this.videoEndedCallback_);
61 | }
62 | };
63 |
64 | /**
65 | * Logs messages to the console.
66 | * @param {string} message
67 | */
68 | Application.prototype.log = function(message) {
69 | console.log(message);
70 | this.console_.innerHTML = this.console_.innerHTML + ' ' + message;
71 | };
72 |
73 | /**
74 | * Handles resuming content following ads.
75 | */
76 | Application.prototype.resumeAfterAd = function() {
77 | this.videoPlayer_.play();
78 | this.adsActive_ = false;
79 | this.updateChrome_();
80 | };
81 |
82 | /**
83 | * Handles pausing content for ad breaks.
84 | */
85 | Application.prototype.pauseForAd = function() {
86 | this.adsActive_ = true;
87 | this.playing_ = true;
88 | this.videoPlayer_.pause();
89 | this.updateChrome_();
90 | };
91 |
92 | /**
93 | * Pauses video on ad clicks.
94 | */
95 | Application.prototype.adClicked = function() {
96 | this.updateChrome_();
97 | };
98 |
99 | /**
100 | * Function binding helper function.
101 | * @param {!Object} thisObj object to bind function.
102 | * @param {!Function} fn function being bound to object.
103 | * @return {!Function} returns the bound function.
104 | */
105 | Application.prototype.bind_ = function(thisObj, fn) {
106 | return function() {
107 | fn.apply(thisObj, arguments);
108 | };
109 | };
110 |
111 | /**
112 | * Set the sample ad tag as the ad tag box's value.
113 | */
114 | Application.prototype.onSampleAdTagClick_ = function() {
115 | this.adTagBox_.value = this.SAMPLE_AD_TAG_;
116 | };
117 |
118 | /**
119 | * Handles pausing the content or ads for ad clicks.
120 | */
121 | Application.prototype.onClick_ = function() {
122 | if (!this.adsDone_) {
123 | if (this.adTagBox_.value == '') {
124 | this.log('Error: Fill in an ad tag');
125 | return;
126 | } else {
127 | this.adTagUrl_ = this.adTagBox_.value;
128 | }
129 | // The user clicked/tapped - inform the ads controller that this code
130 | // is being run in a user action thread.
131 | this.ads_.initialUserAction();
132 | // At the same time, initialize the content player as well.
133 | // When content is loaded, we'll issue the ad request to prevent it
134 | // from interfering with the initialization. See
135 | // https://developers.google.com/interactive-media-ads/docs/sdks/html5/v3/ads#iosvideo
136 | // for more information.
137 | this.videoPlayer_.preloadContent(this.bind_(this, this.loadAds_));
138 | this.adsDone_ = true;
139 | return;
140 | }
141 |
142 | if (this.adsActive_) {
143 | if (this.playing_) {
144 | this.ads_.pause();
145 | } else {
146 | this.ads_.resume();
147 | }
148 | } else {
149 | if (this.playing_) {
150 | this.videoPlayer_.pause();
151 | } else {
152 | this.videoPlayer_.play();
153 | }
154 | }
155 |
156 | this.playing_ = !this.playing_;
157 |
158 | this.updateChrome_();
159 | };
160 |
161 | /**
162 | * Handles making the video fullscreen, or renturning from fullscreen.
163 | */
164 | Application.prototype.onFullscreenClick_ = function() {
165 | if (this.fullscreen) {
166 | // The video is currently in fullscreen mode
167 | const cancelFullscreen = document.exitFullscreen ||
168 | document.exitFullScreen || document.webkitCancelFullScreen ||
169 | document.mozCancelFullScreen;
170 | if (cancelFullscreen) {
171 | cancelFullscreen.call(document);
172 | } else {
173 | this.onFullscreenChange_();
174 | }
175 | } else {
176 | // Try to enter fullscreen mode in the browser
177 | const requestFullscreen = document.documentElement.requestFullscreen ||
178 | document.documentElement.webkitRequestFullscreen ||
179 | document.documentElement.mozRequestFullscreen ||
180 | document.documentElement.requestFullScreen ||
181 | document.documentElement.webkitRequestFullScreen ||
182 | document.documentElement.mozRequestFullScreen;
183 | if (requestFullscreen) {
184 | this.fullscreenWidth = window.screen.width;
185 | this.fullscreenHeight = window.screen.height;
186 | requestFullscreen.call(document.documentElement);
187 | } else {
188 | this.fullscreenWidth = window.innerWidth;
189 | this.fullscreenHeight = window.innerHeight;
190 | this.onFullscreenChange_();
191 | }
192 | }
193 | requestFullscreen.call(document.documentElement);
194 | };
195 |
196 | /**
197 | * Handles updating the play button image.
198 | */
199 | Application.prototype.updateChrome_ = function() {
200 | if (this.playing_) {
201 | this.playButton_.textContent = 'II';
202 | } else {
203 | // Unicode play symbol.
204 | this.playButton_.textContent = String.fromCharCode(9654);
205 | }
206 | };
207 |
208 | /**
209 | * Removes the 'loadedmetadata' listener and makes the ad request.
210 | */
211 | Application.prototype.loadAds_ = function() {
212 | this.videoPlayer_.removePreloadListener();
213 | this.ads_.requestAds(this.adTagUrl_);
214 | };
215 |
216 | /**
217 | * Handles resizing ads and content during fullscreen button clicks.
218 | */
219 | Application.prototype.onFullscreenChange_ = function() {
220 | if (this.fullscreen) {
221 | // The user just exited fullscreen
222 | // Resize the ad container
223 | this.ads_.resize(this.videoPlayer_.width, this.videoPlayer_.height);
224 | // Return the video to its original size and position
225 | this.videoPlayer_.resize(
226 | 'relative', '', '', this.videoPlayer_.width, this.videoPlayer_.height);
227 | this.fullscreen = false;
228 | } else {
229 | // The fullscreen button was just clicked
230 | // Resize the ad container
231 | const width = this.fullscreenWidth;
232 | const height = this.fullscreenHeight;
233 | this.makeAdsFullscreen_();
234 | // Make the video take up the entire screen
235 | this.videoPlayer_.resize('absolute', 0, 0, width, height);
236 | this.fullscreen = true;
237 | }
238 | };
239 |
240 | /**
241 | * Resizes ads for fullscreen.
242 | */
243 | Application.prototype.makeAdsFullscreen_ = function() {
244 | this.ads_.resize(this.fullscreenWidth, this.fullscreenHeight);
245 | };
246 |
247 | /**
248 | * Makes call to update UI on content ending.
249 | */
250 | Application.prototype.onContentEnded_ = function() {
251 | this.ads_.contentEnded();
252 | };
253 |
--------------------------------------------------------------------------------
/advanced/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
81 |
88 |
89 |
90 |
--------------------------------------------------------------------------------
/advanced/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: arial, verdana, sans-serif;
3 | overflow: hidden;
4 | }
5 |
6 | header, footer { text-align:center; width: 100%; }
7 |
8 | header {
9 | height: 40px;
10 | font-size: 40px;
11 | font-weight: bold;
12 | margin-top: 20px;
13 | margin-bottom: 20px;
14 | }
15 |
16 | footer {
17 | margin-top: 20px;
18 | }
19 |
20 | #container {
21 | margin-left: auto;
22 | margin-right: auto;
23 | width: 728px;
24 | }
25 |
26 | #videoplayer {
27 | position: relative;
28 | background-color: #000;
29 | border-radius: 5px;
30 | box-shadow: 0px 0px 20px rgba(50, 50, 50, 0.95);
31 | border: 2px #ccc solid;
32 | width: 640px;
33 | height: 360px;
34 | margin-left: auto;
35 | margin-right: auto;
36 | margin-top: 20px;
37 | }
38 |
39 | #content-wrapper {
40 | position:relative;
41 | top: 0px;
42 | left: 0px
43 | }
44 |
45 | #playpause {
46 | position: absolute;
47 | left: 20px;
48 | bottom: 20px;
49 | height: 40px;
50 | width: 100px;
51 | border-style: none;
52 | font-weight: bold;
53 | font-size: 25px;
54 | opacity: 0.5;
55 | background-color: #fff;
56 | border-radius: 5px;
57 | border: 1px transparent solid;
58 | color: #000;
59 | cursor: pointer;
60 | line-height: 0;
61 | }
62 |
63 | #playpause:hover {
64 | border: 1px #f00 solid;
65 | color: #f00;
66 | }
67 |
68 | #fullscreen {
69 | position: absolute;
70 | bottom: 20px;
71 | left: 140px;
72 | height: 40px;
73 | width: 100px;
74 | border-style: none;
75 | font-weight: bold;
76 | font-size: 25px;
77 | opacity: 0.5;
78 | background-color: #fff;
79 | border-radius: 5px;
80 | border: 1px transparent solid;
81 | color: #000;
82 | cursor: pointer;
83 | line-height: 0;
84 | }
85 |
86 | #fullscreen:hover {
87 | border: 1px #f00 solid;
88 | color: #f00;
89 | }
90 |
91 | #content {
92 | overflow: hidden;
93 | }
94 |
95 | #content, #adcontainer {
96 | position: absolute;
97 | top: 0px;
98 | left: 0px;
99 | width: 640px;
100 | height: 360px;
101 | }
102 |
103 | #console {
104 | font-family: courier, monospace;
105 | font-size: 12px;
106 | margin-top: 20px;
107 | height: 200px;
108 | width: 630px;
109 | padding: 5px;
110 | border: 1px #ccc solid;
111 | overflow-y: scroll;
112 | margin-left: auto;
113 | margin-right: auto;
114 | }
115 |
116 | #companionDiv, #customClick {
117 | width: 728px;
118 | height: 90px;
119 | margin-top: 20px;
120 | }
121 |
122 | #customClick {
123 | background-color: #807F80;
124 | display: none;
125 | text-align: center;
126 | }
127 |
128 | #customClickTextWrapper {
129 | display: table-cell;
130 | vertical-align: middle;
131 | }
132 |
133 | .urlLink {
134 | color: blue;
135 | text-decoration: underline;
136 | cursor: pointer;
137 | }
138 |
--------------------------------------------------------------------------------
/advanced/video_player.js:
--------------------------------------------------------------------------------
1 | // Copyright 2013 Google Inc. All Rights Reserved.
2 | // You may study, modify, and use this example for any purpose.
3 | // Note that this example is provided "as is", WITHOUT WARRANTY
4 | // of any kind either expressed or implied.
5 |
6 | /**
7 | * Handles video player functionality.
8 | */
9 | const VideoPlayer = function() {
10 | this.contentPlayer = document.getElementById('content');
11 | this.adContainer = document.getElementById('adcontainer');
12 | this.videoPlayerContainer_ = document.getElementById('videoplayer');
13 |
14 | this.width = 640;
15 | this.height = 360;
16 | };
17 |
18 | VideoPlayer.prototype.preloadContent = function(contentLoadedAction) {
19 | // If this is the initial user action on iOS or Android device,
20 | // simulate playback to enable the video element for later program-triggered
21 | // playback.
22 | if (this.isMobilePlatform()) {
23 | this.preloadListener_ = contentLoadedAction;
24 | this.contentPlayer.addEventListener(
25 | 'loadedmetadata', contentLoadedAction, false);
26 | this.contentPlayer.load();
27 | } else {
28 | contentLoadedAction();
29 | }
30 | };
31 |
32 | VideoPlayer.prototype.removePreloadListener = function() {
33 | if (this.preloadListener_) {
34 | this.contentPlayer.removeEventListener(
35 | 'loadedmetadata', this.preloadListener_, false);
36 | this.preloadListener_ = null;
37 | }
38 | };
39 |
40 | VideoPlayer.prototype.play = function() {
41 | this.contentPlayer.play();
42 | };
43 |
44 | VideoPlayer.prototype.pause = function() {
45 | this.contentPlayer.pause();
46 | };
47 |
48 | VideoPlayer.prototype.isMobilePlatform = function() {
49 | return this.contentPlayer.paused &&
50 | (navigator.userAgent.match(/(iPod|iPhone|iPad)/) ||
51 | navigator.userAgent.toLowerCase().indexOf('android') > -1);
52 | };
53 |
54 | VideoPlayer.prototype.resize = function(position, top, left, width, height) {
55 | this.videoPlayerContainer_.style.position = position;
56 | this.videoPlayerContainer_.style.top = top + 'px';
57 | this.videoPlayerContainer_.style.left = left + 'px';
58 | this.videoPlayerContainer_.style.width = width + 'px';
59 | this.videoPlayerContainer_.style.height = height + 'px';
60 | this.contentPlayer.style.width = width + 'px';
61 | this.contentPlayer.style.height = height + 'px';
62 | };
63 |
64 | VideoPlayer.prototype.registerVideoEndedCallback = function(callback) {
65 | this.contentPlayer.addEventListener('ended', callback, false);
66 | };
67 |
68 | VideoPlayer.prototype.removeVideoEndedCallback = function(callback) {
69 | this.contentPlayer.removeEventListener('ended', callback, false);
70 | };
71 |
--------------------------------------------------------------------------------
/attempt_to_autoplay/ads.js:
--------------------------------------------------------------------------------
1 | // Copyright 2017 Google Inc.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | let adsManager;
16 | let adsLoader;
17 | let adDisplayContainer;
18 | let playButton;
19 | let videoContent;
20 | let adsInitialized;
21 | let autoplayAllowed;
22 | let autoplayRequiresMuted;
23 |
24 | /**
25 | * Initializes IMA setup.
26 | */
27 | function initDesktopAutoplayExample() {
28 | videoContent = document.getElementById('contentElement');
29 | playButton = document.getElementById('playButton');
30 | playButton.addEventListener('click', () => {
31 | // Initialize the container. Must be done through a user action where
32 | // autoplay is not allowed.
33 | adDisplayContainer.initialize();
34 | adsInitialized = true;
35 | videoContent.load();
36 | playAds();
37 | });
38 | setUpIMA();
39 | // Check if autoplay is supported.
40 | checkAutoplaySupport();
41 | }
42 |
43 | /**
44 | * Attempts autoplay and handles success and failure cases.
45 | */
46 | function checkAutoplaySupport() {
47 | // Test for autoplay support with our content player.
48 | const playPromise = videoContent.play();
49 | if (playPromise !== undefined) {
50 | playPromise.then(onAutoplayWithSoundSuccess).catch(onAutoplayWithSoundFail);
51 | }
52 | }
53 |
54 | /**
55 | * Handles case where autoplay succeeded with sound.
56 | */
57 | function onAutoplayWithSoundSuccess() {
58 | // If we make it here, unmuted autoplay works.
59 | videoContent.pause();
60 | autoplayAllowed = true;
61 | autoplayRequiresMuted = false;
62 | autoplayChecksResolved();
63 | }
64 |
65 | /**
66 | * Handles case where autoplay fails with sound.
67 | */
68 | function onAutoplayWithSoundFail() {
69 | // Unmuted autoplay failed. Now try muted autoplay.
70 | checkMutedAutoplaySupport();
71 | }
72 |
73 | /**
74 | * Checks if video can autoplay while muted.
75 | */
76 | function checkMutedAutoplaySupport() {
77 | videoContent.volume = 0;
78 | videoContent.muted = true;
79 | const playPromise = videoContent.play();
80 | if (playPromise !== undefined) {
81 | playPromise.then(onMutedAutoplaySuccess).catch(onMutedAutoplayFail);
82 | }
83 | }
84 |
85 | /**
86 | * Handles case where autoplay succeeded while muted.
87 | */
88 | function onMutedAutoplaySuccess() {
89 | // If we make it here, muted autoplay works but unmuted autoplay does not.
90 | videoContent.pause();
91 | autoplayAllowed = true;
92 | autoplayRequiresMuted = true;
93 | autoplayChecksResolved();
94 | }
95 |
96 | /**
97 | * Handles case where autoplay failed while muted.
98 | */
99 | function onMutedAutoplayFail() {
100 | // Both muted and unmuted autoplay failed. Fall back to click to play.
101 | videoContent.volume = 1;
102 | videoContent.muted = false;
103 | autoplayAllowed = false;
104 | autoplayRequiresMuted = false;
105 | autoplayChecksResolved();
106 | }
107 |
108 | /**
109 | * Sets up IMA ad display container, ads loader, and makes an ad request.
110 | */
111 | function setUpIMA() {
112 | // Create the ad display container.
113 | createAdDisplayContainer();
114 | // Create ads loader.
115 | adsLoader = new google.ima.AdsLoader(adDisplayContainer);
116 | // Listen and respond to ads loaded and error events.
117 | adsLoader.addEventListener(
118 | google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED,
119 | onAdsManagerLoaded, false);
120 | adsLoader.addEventListener(
121 | google.ima.AdErrorEvent.Type.AD_ERROR, onAdError, false);
122 |
123 | // An event listener to tell the SDK that our content video
124 | // is completed so the SDK can play any post-roll ads.
125 | videoContent.onended = contentEndedListener;
126 | }
127 |
128 | /**
129 | * Handles content ending and calls adsLoader.contentComplete()
130 | */
131 | function contentEndedListener() {
132 | videoContent.onended = null;
133 | if (adsLoader) {
134 | adsLoader.contentComplete();
135 | }
136 | }
137 |
138 | /**
139 | * Builds an ad request and uses it to request ads.
140 | */
141 | function autoplayChecksResolved() {
142 | // Request video ads.
143 | const adsRequest = new google.ima.AdsRequest();
144 | adsRequest.adTagUrl = 'https://pubads.g.doubleclick.net/gampad/ads?' +
145 | 'iu=/21775744923/external/single_ad_samples&sz=640x480&' +
146 | 'cust_params=sample_ct%3Dlinear&ciu_szs=300x250%2C728x90&' +
147 | 'gdfp_req=1&output=vast&unviewed_position_start=1&env=vp&' +
148 | 'impl=s&correlator=';
149 |
150 | // Specify the linear and nonlinear slot sizes. This helps the SDK to
151 | // select the correct creative if multiple are returned.
152 | adsRequest.linearAdSlotWidth = 640;
153 | adsRequest.linearAdSlotHeight = 400;
154 |
155 | adsRequest.nonLinearAdSlotWidth = 640;
156 | adsRequest.nonLinearAdSlotHeight = 150;
157 |
158 | adsRequest.setAdWillAutoPlay(autoplayAllowed);
159 | adsRequest.setAdWillPlayMuted(autoplayRequiresMuted);
160 | adsLoader.requestAds(adsRequest);
161 | }
162 |
163 | /**
164 | * Sets the 'adContainer' div as the IMA ad display container.
165 | */
166 | function createAdDisplayContainer() {
167 | // We assume the adContainer is the DOM id of the element that will house
168 | // the ads.
169 | adDisplayContainer = new google.ima.AdDisplayContainer(
170 | document.getElementById('adContainer'), videoContent);
171 | }
172 |
173 | /**
174 | * Loads the video content and initializes IMA ad playback.
175 | */
176 | function playAds() {
177 | try {
178 | if (!adsInitialized) {
179 | adDisplayContainer.initialize();
180 | adsInitialized = true;
181 | }
182 | // Initialize the ads manager. Ad rules playlist will start at this time.
183 | adsManager.init(640, 360);
184 | // Call play to start showing the ad. Single video and overlay ads will
185 | // start at this time; the call will be ignored for ad rules.
186 | adsManager.start();
187 | } catch (adError) {
188 | // An error may be thrown if there was a problem with the VAST response.
189 | videoContent.play();
190 | }
191 | }
192 |
193 | /**
194 | * Handles the ad manager loading and sets ad event listeners.
195 | * @param {!google.ima.AdsManagerLoadedEvent} adsManagerLoadedEvent
196 | */
197 | function onAdsManagerLoaded(adsManagerLoadedEvent) {
198 | // Get the ads manager.
199 | const adsRenderingSettings = new google.ima.AdsRenderingSettings();
200 | adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true;
201 | // videoContent should be set to the content video element.
202 | adsManager =
203 | adsManagerLoadedEvent.getAdsManager(videoContent, adsRenderingSettings);
204 | // Mute the ad if doing muted autoplay.
205 | const adVolume = (autoplayAllowed && autoplayRequiresMuted) ? 0 : 1;
206 | adsManager.setVolume(adVolume);
207 |
208 | // Add listeners to the required events.
209 | adsManager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, onAdError);
210 | adsManager.addEventListener(
211 | google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED, onContentPauseRequested);
212 | adsManager.addEventListener(
213 | google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED,
214 | onContentResumeRequested);
215 | adsManager.addEventListener(
216 | google.ima.AdEvent.Type.ALL_ADS_COMPLETED, onAdEvent);
217 |
218 | // Listen to any additional events, if necessary.
219 | adsManager.addEventListener(google.ima.AdEvent.Type.LOADED, onAdEvent);
220 | adsManager.addEventListener(google.ima.AdEvent.Type.STARTED, onAdEvent);
221 | adsManager.addEventListener(google.ima.AdEvent.Type.COMPLETE, onAdEvent);
222 |
223 | if (autoplayAllowed) {
224 | playAds();
225 | } else {
226 | playButton.style.display = 'block';
227 | }
228 | }
229 |
230 | /**
231 | * Handles actions taken in response to ad events.
232 | * @param {!google.ima.AdEvent} adEvent
233 | */
234 | function onAdEvent(adEvent) {
235 | // Retrieve the ad from the event. Some events (for example,
236 | // ALL_ADS_COMPLETED) don't have ad object associated.
237 | const ad = adEvent.getAd();
238 | switch (adEvent.type) {
239 | case google.ima.AdEvent.Type.LOADED:
240 | // This is the first event sent for an ad - it is possible to
241 | // determine whether the ad is a video ad or an overlay.
242 | if (!ad.isLinear()) {
243 | videoContent.play();
244 | }
245 | break;
246 | }
247 | }
248 |
249 | /**
250 | * Handles ad errors.
251 | * @param {!google.ima.AdErrorEvent} adErrorEvent
252 | */
253 | function onAdError(adErrorEvent) {
254 | // Handle the error logging.
255 | console.log(adErrorEvent.getError());
256 | adsManager.destroy();
257 | // Fall back to playing content.
258 | videoContent.play();
259 | }
260 |
261 | /**
262 | * Pauses video content and sets up ad UI.
263 | */
264 | function onContentPauseRequested() {
265 | videoContent.pause();
266 | videoContent.onended = null;
267 | }
268 |
269 | /**
270 | * Resumes video content and removes ad UI.
271 | */
272 | function onContentResumeRequested() {
273 | videoContent.play();
274 | videoContent.onended = contentEndedListener;
275 | }
276 |
--------------------------------------------------------------------------------
/attempt_to_autoplay/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | IMA HTML5 Attempt to Autoplay
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | This player does the following:
12 |
13 |
Attempt to autoplay unmuted. If that works, do so. If not, move on to step 2.
14 |
Attempt to autoplay muted. If that works, do so. If not, move on to step 3.
15 |
Fall back to click-to-play.
16 |
17 |
18 |
19 |
20 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/attempt_to_autoplay/style.css:
--------------------------------------------------------------------------------
1 | #mainContainer {
2 | position: relative;
3 | width: 640px;
4 | height: 360px;
5 | }
6 |
7 | #content, #adContainer {
8 | position: absolute;
9 | top: 0px;
10 | left: 0px;
11 | width: 640px;
12 | height: 360px;
13 | }
14 |
15 | #contentElement {
16 | width: 640px;
17 | height: 360px;
18 | overflow: hidden;
19 | }
20 |
21 | #playButton {
22 | display: none;
23 | margin-top:10px;
24 | vertical-align: top;
25 | width: 350px;
26 | height: 60px;
27 | padding: 0;
28 | font-size: 22px;
29 | font-family: sans-serif;
30 | color: white;
31 | text-align: center;
32 | text-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
33 | background: #2c3e50;
34 | border: 0;
35 | border-bottom: 2px solid #22303f;
36 | cursor: pointer;
37 | -webkit-box-shadow: inset 0 -2px #22303f;
38 | box-shadow: inset 0 -2px #22303f;
39 | }
40 |
--------------------------------------------------------------------------------
/audio_in_video/ads.js:
--------------------------------------------------------------------------------
1 | let videoElement;
2 | let playButton;
3 | let adsLoaded = false;
4 | let adContainer;
5 | let adDisplayContainer;
6 | let adsLoader;
7 | let adsManager;
8 | let adsActive;
9 | let isPlaying;
10 | let contentCompleted = false;
11 | const RIGHT_POINTING_TRIANGLE_CHAR_CODE = 9654;
12 | const adTagUrl =
13 | 'https://pubads.g.doubleclick.net/gampad/ads?iu=/6075/Rahul_AdUnit_Test_1&description_url=[placeholder]&tfcd=0&npa=0&ad_type=audio_video&sz=640x360&ciu_szs=640x360&cust_params=yt_channel_id%3Drtryuyuu&gdfp_req=1&output=vast&unviewed_position_start=1&env=vp&impl=s&correlator=';
14 |
15 | // On window load, attach an event to the play button click
16 | // that triggers playback on the video element
17 | window.addEventListener('load', function(event) {
18 | videoElement = document.getElementById('video-element');
19 | videoElement.addEventListener('play', function(event) {
20 | loadAds(event);
21 | });
22 | initializeIMA();
23 | playButton = document.getElementById('play-pause');
24 | playButton.addEventListener('click', playPause);
25 | });
26 |
27 | window.addEventListener('resize', function(event) {
28 | console.log('window resized');
29 | if (adsManager) {
30 | let width = videoElement.clientWidth;
31 | let height = videoElement.clientHeight;
32 | adsManager.resize(width, height);
33 | }
34 | });
35 |
36 | /** Implementing play&pause functionality for player control */
37 | function playPause() {
38 | if (adsActive) {
39 | if (isPlaying) {
40 | adsManager.pause();
41 | } else {
42 | adsManager.resume();
43 | }
44 | } else {
45 | if (isPlaying) {
46 | videoElement.pause();
47 | } else {
48 | videoElement.play();
49 | }
50 | }
51 |
52 | isPlaying = !isPlaying;
53 |
54 | if (isPlaying) {
55 | playButton.textContent = '||';
56 | } else {
57 | playButton.textContent =
58 | String.fromCharCode(RIGHT_POINTING_TRIANGLE_CHAR_CODE);
59 | }
60 | }
61 |
62 | /** Initializing IMA SDK */
63 | function initializeIMA() {
64 | console.log('initializing IMA');
65 | adContainer = document.getElementById('ad-container');
66 | adDisplayContainer =
67 | new google.ima.AdDisplayContainer(adContainer, videoElement);
68 |
69 | // Set the feature flag and default image URL for the audio poster feature.
70 | google.ima.settings.setFeatureFlags(
71 | {
72 | 'audioPosterImageEnabled': true,
73 | 'audioPosterImageDefaultUrl':
74 | 'https://storage.googleapis.com/interactive-media-ads/images/ima_default_audio_sample.png'
75 | });
76 | adsLoader = new google.ima.AdsLoader(adDisplayContainer);
77 | adsLoader.addEventListener(
78 | google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED,
79 | onAdsManagerLoaded, false);
80 | adsLoader.addEventListener(
81 | google.ima.AdErrorEvent.Type.AD_ERROR, onAdError, false);
82 |
83 | // Let the AdsLoader know when the video has ended
84 | videoElement.addEventListener('ended', function() {
85 | adsLoader.contentComplete();
86 | contentCompleted = true;
87 | });
88 |
89 | const adsRequest = new google.ima.AdsRequest();
90 | adsRequest.adTagUrl = adTagUrl;
91 | // Specify the linear and nonlinear slot sizes. This helps the SDK to
92 | // select the correct creative if multiple are returned.
93 | adsRequest.linearAdSlotWidth = videoElement.clientWidth;
94 | adsRequest.linearAdSlotHeight = videoElement.clientHeight;
95 | adsRequest.nonLinearAdSlotWidth = videoElement.clientWidth;
96 | adsRequest.nonLinearAdSlotHeight = videoElement.clientHeight / 3;
97 |
98 | // Pass the request to the adsLoader to request ads
99 | adsLoader.requestAds(adsRequest);
100 | }
101 |
102 | /**
103 | * Loading Ads
104 | * @param {!event} event The event triggering ad loading
105 | */
106 | function loadAds(event) {
107 | // Prevent this function from running on if there are already ads loaded
108 | if (adsLoaded) {
109 | return;
110 | }
111 | adsLoaded = true;
112 |
113 | // Prevent triggering immediate playback when ads are loading
114 | event.preventDefault();
115 |
116 | console.log('loading ads');
117 |
118 | // Initialize the container. Must be done through a user action on mobile
119 | // devices.
120 | videoElement.load();
121 | adDisplayContainer.initialize();
122 |
123 | const width = videoElement.clientWidth;
124 | const height = videoElement.clientHeight;
125 | try {
126 | adsManager.init(width, height);
127 | adsManager.start();
128 | } catch (adError) {
129 | // Play the video without ads, if an error occurs
130 | console.log('AdsManager could not be started');
131 | videoElement.play();
132 | isPlaying = true;
133 | }
134 | }
135 |
136 | /**
137 | * Handling adsManagerLoaded event
138 | * @param {!adsManagerLoadedEvent} adsManagerLoadedEvent The event indicating
139 | * adsManager has been loaded
140 | */
141 | function onAdsManagerLoaded(adsManagerLoadedEvent) {
142 | // Instantiate the AdsManager from the adsLoader response and pass it the
143 | // video element
144 | adsManager = adsManagerLoadedEvent.getAdsManager(videoElement);
145 |
146 | adsManager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, onAdError);
147 | adsManager.addEventListener(
148 | google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED, onContentPauseRequested);
149 | adsManager.addEventListener(
150 | google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED,
151 | onContentResumeRequested);
152 | adsManager.addEventListener(google.ima.AdEvent.Type.LOADED, onAdLoaded);
153 | adsManager.addEventListener(google.ima.AdEvent.Type.PAUSED, onAdEvent);
154 | adsManager.addEventListener(google.ima.AdEvent.Type.SKIPPED, onAdEvent);
155 | adsManager.addEventListener(google.ima.AdEvent.Type.RESUMED, onAdEvent);
156 | adsManager.addEventListener(google.ima.AdEvent.Type.STARTED, onAdEvent);
157 | adsManager.addEventListener(google.ima.AdEvent.Type.COMPLETE, onAdEvent);
158 | adsManager.addEventListener(
159 | google.ima.AdEvent.Type.ALL_ADS_COMPLETED, onAdEvent);
160 | }
161 |
162 | /**
163 | * Handing error event
164 | * @param {!adErrorEvent} adErrorEvent Event indicating ad error
165 | */
166 | function onAdError(adErrorEvent) {
167 | // Handle the error logging.
168 | console.log(adErrorEvent.getError());
169 | if (adsManager) {
170 | adsManager.destroy();
171 | }
172 | }
173 |
174 | /** Handing content pause */
175 | function onContentPauseRequested() {
176 | videoElement.pause();
177 | isPlaying = false;
178 | }
179 |
180 | /** Handing content resume */
181 | function onContentResumeRequested() {
182 | adsActive = false;
183 | // condition to disable replaying of content video after post-roll ads
184 | if (!contentCompleted) {
185 | videoElement.play();
186 | isPlaying = true;
187 | }
188 | }
189 |
190 | /**
191 | * Handing ad loaded event
192 | * @param {!adEvent} adEvent Ad Event
193 | */
194 | function onAdLoaded(adEvent) {
195 | const ad = adEvent.getAd();
196 | if (!ad.isLinear()) {
197 | videoElement.play();
198 | isPlaying = true;
199 | }
200 | }
201 |
202 | /**
203 | * Handing ad play-related events
204 | * @param {!adEvent} adEvent Ad Event
205 | */
206 | function onAdEvent(adEvent) {
207 | // Retrieve the ad from the event. Some events (for example,
208 | // ALL_ADS_COMPLETED) don't have ad object associated.
209 | const ad = adEvent.getAd();
210 | switch (adEvent.type) {
211 | case google.ima.AdEvent.Type.LOADED:
212 | // This is the first event sent for an ad - it is possible to
213 | // determine whether the ad is a video ad or an overlay.
214 | if (!ad.isLinear()) {
215 | // Position AdDisplayContainer correctly for overlay.
216 | // Use ad.width and ad.height.
217 | videoContent.play();
218 | }
219 | break;
220 | case google.ima.AdEvent.Type.STARTED:
221 | adsActive = true;
222 | isPlaying = true;
223 | break;
224 | case google.ima.AdEvent.Type.PAUSED:
225 | isPlaying = false;
226 | break;
227 | case google.ima.AdEvent.Type.RESUMED:
228 | isPlaying = true;
229 | break;
230 | case google.ima.AdEvent.Type.ALL_ADS_COMPLETED:
231 | // reset the play button after all ads and content have completed
232 | if (contentCompleted) {
233 | isPlaying = false;
234 | playButton.textContent = String.fromCharCode(9654);
235 | }
236 | break;
237 | }
238 | }
239 |
--------------------------------------------------------------------------------
/audio_in_video/image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/googleads/googleads-ima-html5/bc910dc76a00f46b81483c81ee057da696933799/audio_in_video/image.png
--------------------------------------------------------------------------------
/audio_in_video/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Audio in Video Demo
5 |
6 |
7 |
8 |
9 |
10 |
Audio Ads in Video Player Demo
11 |
12 |
13 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/audio_in_video/style.css:
--------------------------------------------------------------------------------
1 | h2,
2 | p {
3 | text-align: center;
4 | }
5 |
6 | #page-content {
7 | position: relative;
8 | /* this element's width controls the effective height */
9 | /* of the video container's padding-bottom */
10 | max-width: 640px;
11 | margin: 10px auto;
12 | }
13 |
14 | #video-container {
15 | position: relative;
16 | /* forces the container to match a 16x9 aspect ratio */
17 | /* replace with 75% for a 4:3 aspect ratio, if needed */
18 | padding-bottom: 56.25%;
19 | }
20 |
21 | #video-element {
22 | /* forces the contents to fill the container */
23 | position: absolute;
24 | top: 0;
25 | left: 0;
26 | width: 100%;
27 | height: 100%;
28 | }
29 |
30 | /* apply the ad-container styling to companionDiv */
31 | #ad-container,
32 | #audio-companion {
33 | position: absolute;
34 | top: 0;
35 | left: 0;
36 | width: 100%;
37 | }
38 |
39 | #video-companion {
40 | display: block;
41 | height: 90px;
42 | width: 728px;
43 | margin-left: auto;
44 | margin-right: auto;
45 | }
46 |
47 | #message {
48 | text-align: center;
49 | }
50 |
51 | #play-pause {
52 | position: absolute;
53 | left: 0;
54 | bottom: 0;
55 | height: 40px;
56 | width: 100px;
57 | border-style: none;
58 | font-weight: bold;
59 | font-size: 25px;
60 | opacity: 0.5;
61 | background-color: #fff;
62 | border-radius: 5px;
63 | border: 1px solid;
64 | color: #000;
65 | cursor: pointer;
66 | line-height: 0;
67 | }
68 |
69 | #play-pause:hover {
70 | border: 1px #f00 solid;
71 | color: #f00;
72 | }
73 |
--------------------------------------------------------------------------------
/live_stream_prefetch/ads.js:
--------------------------------------------------------------------------------
1 | // Copyright 2013 Google Inc. All Rights Reserved.
2 | // You may study, modify, and use this example for any purpose.
3 | // Note that this example is provided "as is", WITHOUT WARRANTY
4 | // of any kind either expressed or implied.
5 |
6 | let adsManager;
7 | let adsLoader;
8 | let adDisplayContainer;
9 | let intervalTimer;
10 | let playButton;
11 | let videoContent;
12 | let adDisplayContainerInitialized;
13 |
14 | const AD_REQUEST_INTERVAL = 30;
15 |
16 | /**
17 | * Initializes IMA setup.
18 | */
19 | function init() {
20 | videoContent = document.getElementById('contentElement');
21 | playButton = document.getElementById('playButton');
22 | playButton.addEventListener('click', playAds);
23 | setUpAdsLoader();
24 | // We want a pre-roll, so the first time we request ads set
25 | // liveStreamPrefetchSeconds to 0.
26 | requestAds(0);
27 | }
28 |
29 | /**
30 | * Sets up ads loader and associated event listeners.
31 | */
32 | function setUpAdsLoader() {
33 | // Create the ad display container.
34 | createAdDisplayContainer();
35 | // Create ads loader.
36 | adsLoader = new google.ima.AdsLoader(adDisplayContainer);
37 | // Listen and respond to ads loaded and error events.
38 | adsLoader.addEventListener(
39 | google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED,
40 | onAdsManagerLoaded, false);
41 | adsLoader.addEventListener(
42 | google.ima.AdErrorEvent.Type.AD_ERROR, onAdError, false);
43 |
44 | // An event listener to tell the SDK that our content video
45 | // is completed so the SDK can play any post-roll ads.
46 | const contentEndedListener = function() {
47 | adsLoader.contentComplete();
48 | };
49 | videoContent.onended = contentEndedListener;
50 | }
51 |
52 | /**
53 | * Makes an ad request with a prefetch time.
54 | * @param {number} liveStreamPrefetchSeconds prefetch time in seconds.
55 | */
56 | function requestAds(liveStreamPrefetchSeconds) {
57 | if (adsLoader) {
58 | adsLoader.contentComplete();
59 | }
60 | // Request video ads.
61 | const adsRequest = new google.ima.AdsRequest();
62 | adsRequest.adTagUrl = 'https://pubads.g.doubleclick.net/gampad/ads?' +
63 | 'iu=/21775744923/external/single_ad_samples&sz=640x480&' +
64 | 'cust_params=sample_ct%3Dlinear&ciu_szs=300x250%2C728x90&' +
65 | 'gdfp_req=1&output=vast&unviewed_position_start=1&env=vp&impl=s&' +
66 | 'correlator=';
67 |
68 | // Specify the linear and nonlinear slot sizes. This helps the SDK to
69 | // select the correct creative if multiple are returned.
70 | adsRequest.linearAdSlotWidth = 640;
71 | adsRequest.linearAdSlotHeight = 400;
72 |
73 | adsRequest.nonLinearAdSlotWidth = 640;
74 | adsRequest.nonLinearAdSlotHeight = 150;
75 |
76 | adsRequest.liveStreamPrefetchSeconds = liveStreamPrefetchSeconds;
77 |
78 | adsLoader.requestAds(adsRequest);
79 | }
80 |
81 | /**
82 | * Sets the 'adContainer' div as the IMA ad display container.
83 | */
84 | function createAdDisplayContainer() {
85 | // We assume the adContainer is the DOM id of the element that will house
86 | // the ads.
87 | adDisplayContainer = new google.ima.AdDisplayContainer(
88 | document.getElementById('adContainer'), videoContent);
89 | }
90 |
91 | /**
92 | * Loads the video content and initializes IMA ad playback.
93 | */
94 | function playAds() {
95 | if (!adDisplayContainerInitialized) {
96 | // Initialize the container. Must be done through a user action on mobile
97 | // devices.
98 | videoContent.load();
99 | adDisplayContainer.initialize();
100 | adDisplayContainerInitialized = true;
101 | }
102 |
103 | try {
104 | // Initialize the ads manager. Ad rules playlist will start at this time.
105 | adsManager.init(640, 360);
106 | // Call play to start showing the ad. Single video and overlay ads will
107 | // start at this time; the call will be ignored for ad rules.
108 | adsManager.start();
109 | } catch (adError) {
110 | // An error may be thrown if there was a problem with the VAST response.
111 | videoContent.play();
112 | }
113 | }
114 |
115 | /**
116 | * Handles the ad manager loading and sets ad event listeners.
117 | * @param {!google.ima.AdsManagerLoadedEvent} adsManagerLoadedEvent
118 | */
119 | function onAdsManagerLoaded(adsManagerLoadedEvent) {
120 | // Get the ads manager.
121 | const adsRenderingSettings = new google.ima.AdsRenderingSettings();
122 | adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true;
123 | // videoContent should be set to the content video element.
124 | adsManager =
125 | adsManagerLoadedEvent.getAdsManager(videoContent, adsRenderingSettings);
126 |
127 | // Add listeners to the required events.
128 | adsManager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, onAdError);
129 | adsManager.addEventListener(
130 | google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED, onContentPauseRequested);
131 | adsManager.addEventListener(
132 | google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED,
133 | onContentResumeRequested);
134 | adsManager.addEventListener(
135 | google.ima.AdEvent.Type.ALL_ADS_COMPLETED, onAdEvent);
136 |
137 | // Listen to any additional events, if necessary.
138 | adsManager.addEventListener(google.ima.AdEvent.Type.LOADED, onAdEvent);
139 | adsManager.addEventListener(google.ima.AdEvent.Type.STARTED, onAdEvent);
140 | adsManager.addEventListener(google.ima.AdEvent.Type.COMPLETE, onAdEvent);
141 | }
142 |
143 | /**
144 | * Handles actions taken in response to ad events.
145 | * @param {!google.ima.AdEvent} adEvent
146 | */
147 | function onAdEvent(adEvent) {
148 | // Retrieve the ad from the event. Some events (for example,
149 | // ALL_ADS_COMPLETED) don't have ad object associated.
150 | const ad = adEvent.getAd();
151 | switch (adEvent.type) {
152 | case google.ima.AdEvent.Type.LOADED:
153 | // This is the first event sent for an ad - it is possible to
154 | // determine whether the ad is a video ad or an overlay.
155 | if (!ad.isLinear()) {
156 | // Position AdDisplayContainer correctly for overlay.
157 | // Use ad.width and ad.height.
158 | videoContent.play();
159 | }
160 | break;
161 | case google.ima.AdEvent.Type.STARTED:
162 | // This event indicates the ad has started - the video player
163 | // can adjust the UI, for example display a pause button and
164 | // remaining time.
165 | if (ad.isLinear()) {
166 | // For a linear ad, a timer can be started to poll for
167 | // the remaining time.
168 | intervalTimer = setInterval(
169 | function() {
170 | // Example: const remainingTime = adsManager.getRemainingTime();
171 | },
172 | 300); // every 300ms
173 | }
174 | break;
175 | case google.ima.AdEvent.Type.COMPLETE:
176 | // This event indicates the ad has finished - the video player
177 | // can perform appropriate UI actions, such as removing the timer for
178 | // remaining time detection.
179 | if (ad.isLinear()) {
180 | clearInterval(intervalTimer);
181 | }
182 | break;
183 | case google.ima.AdEvent.Type.ALL_ADS_COMPLETED:
184 | // Request ads no later than 5 seconds before our next ad break.
185 | requestAds(AD_REQUEST_INTERVAL - 5);
186 | // Play those ads at the next ad break.
187 | setTimeout(() => {
188 | playAds();
189 | }, AD_REQUEST_INTERVAL * 1000);
190 | break;
191 | }
192 | }
193 |
194 | /**
195 | * Handles ad errors.
196 | * @param {!google.ima.AdErrorEvent} adErrorEvent
197 | */
198 | function onAdError(adErrorEvent) {
199 | // Handle the error logging.
200 | console.log(adErrorEvent.getError());
201 | adsManager.destroy();
202 | }
203 |
204 | /**
205 | * Pauses video content and sets up ad UI.
206 | */
207 | function onContentPauseRequested() {
208 | videoContent.pause();
209 | // This function is where you should setup UI for showing ads (for example,
210 | // display ad timer countdown, disable seeking and more.)
211 | // setupUIForAds();
212 | }
213 |
214 | /**
215 | * Resumes video content and removes ad UI.
216 | */
217 | function onContentResumeRequested() {
218 | videoContent.play();
219 | // This function is where you should ensure that your UI is ready
220 | // to play content. It is the responsibility of the Publisher to
221 | // implement this function when necessary.
222 | // setupUIForContent();
223 | }
224 |
225 | // Wire UI element references and UI event listeners.
226 | init();
227 |
--------------------------------------------------------------------------------
/live_stream_prefetch/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | IMA HTML5 Simple Demo
4 |
5 |
6 |
7 |
8 |
9 |
10 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/live_stream_prefetch/style.css:
--------------------------------------------------------------------------------
1 | #mainContainer {
2 | position: relative;
3 | width: 640px;
4 | height: 360px;
5 | }
6 |
7 | #content, #adContainer {
8 | position: absolute;
9 | top: 0px;
10 | left: 0px;
11 | width: 640px;
12 | height: 360px;
13 | }
14 |
15 | #contentElement {
16 | width: 640px;
17 | height: 360px;
18 | overflow: hidden;
19 | }
20 |
21 | #playButton {
22 | margin-top:10px;
23 | vertical-align: top;
24 | width: 350px;
25 | height: 60px;
26 | padding: 0;
27 | font-size: 22px;
28 | color: white;
29 | text-align: center;
30 | text-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
31 | background: #2c3e50;
32 | border: 0;
33 | border-bottom: 2px solid #22303f;
34 | cursor: pointer;
35 | -webkit-box-shadow: inset 0 -2px #22303f;
36 | box-shadow: inset 0 -2px #22303f;
37 | }
38 |
--------------------------------------------------------------------------------
/mobile_auto_skippable/ads.js:
--------------------------------------------------------------------------------
1 | // Copyright 2013 Google Inc. All Rights Reserved.
2 | // You may study, modify, and use this example for any purpose.
3 | // Note that this example is provided "as is", WITHOUT WARRANTY
4 | // of any kind either expressed or implied.
5 |
6 | let adsManager;
7 | let adsLoader;
8 | let adDisplayContainer;
9 | let intervalTimer;
10 | let videoContent;
11 |
12 | /**
13 | * Initializes IMA setup.
14 | */
15 | function init() {
16 | videoContent = document.getElementById('contentElement');
17 | setUpIMA();
18 | }
19 |
20 | /**
21 | * Sets up IMA ad display container, ads loader, and makes an ad request.
22 | */
23 | function setUpIMA() {
24 | google.ima.settings.setDisableCustomPlaybackForIOS10Plus(true);
25 | // Create the ad display container.
26 | createAdDisplayContainer();
27 | // Create ads loader.
28 | adsLoader = new google.ima.AdsLoader(adDisplayContainer);
29 | // Listen and respond to ads loaded and error events.
30 | adsLoader.addEventListener(
31 | google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED,
32 | onAdsManagerLoaded, false);
33 | adsLoader.addEventListener(
34 | google.ima.AdErrorEvent.Type.AD_ERROR, onAdError, false);
35 |
36 | // An event listener to tell the SDK that our content video
37 | // is completed so the SDK can play any post-roll ads.
38 | const contentEndedListener = function() {
39 | adsLoader.contentComplete();
40 | };
41 | videoContent.onended = contentEndedListener;
42 |
43 | // Request video ads.
44 | const adsRequest = new google.ima.AdsRequest();
45 | adsRequest.adTagUrl = 'https://pubads.g.doubleclick.net/gampad/ads?' +
46 | 'iu=/21775744923/external/single_preroll_skippable&sz=640x480&' +
47 | 'ciu_szs=300x250%2C728x90&gdfp_req=1&' +
48 | 'output=vast&unviewed_position_start=1&env=vp&impl=s&correlator=';
49 |
50 | // Specify the linear and nonlinear slot sizes. This helps the SDK to
51 | // select the correct creative if multiple are returned.
52 | adsRequest.linearAdSlotWidth = 640;
53 | adsRequest.linearAdSlotHeight = 400;
54 |
55 | adsRequest.nonLinearAdSlotWidth = 640;
56 | adsRequest.nonLinearAdSlotHeight = 150;
57 |
58 | adsLoader.requestAds(adsRequest);
59 | }
60 |
61 | /**
62 | * Sets the 'adContainer' div as the IMA ad display container.
63 | */
64 | function createAdDisplayContainer() {
65 | // We assume the adContainer is the DOM id of the element that will house
66 | // the ads.
67 | adDisplayContainer = new google.ima.AdDisplayContainer(
68 | document.getElementById('adContainer'), videoContent);
69 | }
70 |
71 | /**
72 | * Loads the video content and initializes IMA ad playback.
73 | */
74 | function playAds() {
75 | // Initialize the container. Must be done through a user action on mobile
76 | // devices.
77 | videoContent.load();
78 | adDisplayContainer.initialize();
79 |
80 | try {
81 | // Initialize the ads manager. Ad rules playlist will start at this time.
82 | adsManager.init(640, 360);
83 | // Call play to start showing the ad. Single video and overlay ads will
84 | // start at this time; the call will be ignored for ad rules.
85 | adsManager.start();
86 | } catch (adError) {
87 | // An error may be thrown if there was a problem with the VAST response.
88 | videoContent.play();
89 | }
90 | }
91 |
92 | /**
93 | * Handles the ad manager loading and sets ad event listeners.
94 | * @param {!google.ima.AdsManagerLoadedEvent} adsManagerLoadedEvent
95 | */
96 | function onAdsManagerLoaded(adsManagerLoadedEvent) {
97 | // Get the ads manager.
98 | const adsRenderingSettings = new google.ima.AdsRenderingSettings();
99 | adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true;
100 | // videoContent should be set to the content video element.
101 | adsManager =
102 | adsManagerLoadedEvent.getAdsManager(videoContent, adsRenderingSettings);
103 |
104 | // Add listeners to the required events.
105 | adsManager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, onAdError);
106 | adsManager.addEventListener(
107 | google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED, onContentPauseRequested);
108 | adsManager.addEventListener(
109 | google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED,
110 | onContentResumeRequested);
111 | adsManager.addEventListener(
112 | google.ima.AdEvent.Type.ALL_ADS_COMPLETED, onAdEvent);
113 |
114 | // Listen to any additional events, if necessary.
115 | adsManager.addEventListener(google.ima.AdEvent.Type.LOADED, onAdEvent);
116 | adsManager.addEventListener(google.ima.AdEvent.Type.STARTED, onAdEvent);
117 | adsManager.addEventListener(google.ima.AdEvent.Type.COMPLETE, onAdEvent);
118 |
119 | playAds();
120 | }
121 |
122 | /**
123 | * Handles actions taken in response to ad events.
124 | * @param {!google.ima.AdEvent} adEvent
125 | */
126 | function onAdEvent(adEvent) {
127 | // Retrieve the ad from the event. Some events (for example,
128 | // ALL_ADS_COMPLETED) don't have ad object associated.
129 | const ad = adEvent.getAd();
130 | switch (adEvent.type) {
131 | case google.ima.AdEvent.Type.LOADED:
132 | // This is the first event sent for an ad - it is possible to
133 | // determine whether the ad is a video ad or an overlay.
134 | if (!ad.isLinear()) {
135 | // Position AdDisplayContainer correctly for overlay.
136 | // Use ad.width and ad.height.
137 | videoContent.play();
138 | }
139 | break;
140 | case google.ima.AdEvent.Type.STARTED:
141 | // This event indicates the ad has started - the video player
142 | // can adjust the UI, for example display a pause button and
143 | // remaining time.
144 | if (ad.isLinear()) {
145 | // For a linear ad, a timer can be started to poll for
146 | // the remaining time.
147 | intervalTimer = setInterval(
148 | function() {
149 | // Example: const remainingTime = adsManager.getRemainingTime();
150 | },
151 | 300); // every 300ms
152 | }
153 | break;
154 | case google.ima.AdEvent.Type.COMPLETE:
155 | // This event indicates the ad has finished - the video player
156 | // can perform appropriate UI actions, such as removing the timer for
157 | // remaining time detection.
158 | if (ad.isLinear()) {
159 | clearInterval(intervalTimer);
160 | }
161 | break;
162 | }
163 | }
164 |
165 | /**
166 | * Handles ad errors.
167 | * @param {!google.ima.AdErrorEvent} adErrorEvent
168 | */
169 | function onAdError(adErrorEvent) {
170 | // Handle the error logging.
171 | console.log(adErrorEvent.getError());
172 | adsManager.destroy();
173 | }
174 |
175 | /**
176 | * Pauses video content and sets up ad UI.
177 | */
178 | function onContentPauseRequested() {
179 | videoContent.pause();
180 | // This function is where you should setup UI for showing ads (for example,
181 | // display ad timer countdown, disable seeking and more.)
182 | // setupUIForAds();
183 | }
184 |
185 | /**
186 | * Resumes video content and removes ad UI.
187 | */
188 | function onContentResumeRequested() {
189 | videoContent.play();
190 | // This function is where you should ensure that your UI is ready
191 | // to play content. It is the responsibility of the Publisher to
192 | // implement this function when necessary.
193 | // setupUIForContent();
194 | }
195 |
196 | // Wire UI element references and UI event listeners.
197 | init();
198 |
--------------------------------------------------------------------------------
/mobile_auto_skippable/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | IMA HTML5 Simple Demo
4 |
5 |
6 |
7 |
8 |
9 |
10 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/mobile_auto_skippable/style.css:
--------------------------------------------------------------------------------
1 | #mainContainer {
2 | position: relative;
3 | width: 640px;
4 | height: 360px;
5 | }
6 |
7 | #content, #adContainer {
8 | position: absolute;
9 | top: 0px;
10 | left: 0px;
11 | width: 640px;
12 | height: 360px;
13 | }
14 |
15 | #contentElement {
16 | width: 640px;
17 | height: 360px;
18 | overflow: hidden;
19 | }
20 |
--------------------------------------------------------------------------------
/playlist/ads.js:
--------------------------------------------------------------------------------
1 | // Copyright 2013 Google Inc. All Rights Reserved.
2 | // You may study, modify, and use this example for any purpose.
3 | // Note that this example is provided "as is", WITHOUT WARRANTY
4 | // of any kind either expressed or implied.
5 |
6 | /**
7 | * Shows how to use the IMA SDK to request and display ads.
8 | */
9 | const Ads = function(application, videoPlayer) {
10 | this.application_ = application;
11 | this.videoPlayer_ = videoPlayer;
12 | this.customClickDiv_ = document.getElementById('customClick');
13 | this.linearAdPlaying = false;
14 | google.ima.settings.setVpaidMode(google.ima.ImaSdkSettings.VpaidMode.ENABLED);
15 | this.adDisplayContainer_ = new google.ima.AdDisplayContainer(
16 | this.videoPlayer_.adContainer, this.videoPlayer_.contentPlayer,
17 | this.customClickDiv_);
18 | this.adsLoader_ = new google.ima.AdsLoader(this.adDisplayContainer_);
19 | this.adsManager_ = null;
20 |
21 | this.adsLoader_.addEventListener(
22 | google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED,
23 | this.onAdsManagerLoaded_, false, this);
24 | this.adsLoader_.addEventListener(
25 | google.ima.AdErrorEvent.Type.AD_ERROR, this.onAdError_, false, this);
26 | };
27 |
28 | // On iOS and Android devices, video playback must begin in a user action.
29 | // AdDisplayContainer provides a initialize() API to be called at appropriate
30 | // time.
31 | // This should be called when the user clicks or taps.
32 | Ads.prototype.initialUserAction = function() {
33 | this.adDisplayContainer_.initialize();
34 | this.videoPlayer_.contentPlayer.load();
35 | };
36 |
37 | Ads.prototype.requestAds = function(adTagUrl) {
38 | this.contentCompleteCalled = false;
39 | this.allAdsCompleted = false;
40 | const adsRequest = new google.ima.AdsRequest();
41 | adsRequest.adTagUrl = adTagUrl;
42 | adsRequest.linearAdSlotWidth = this.videoPlayer_.width;
43 | adsRequest.linearAdSlotHeight = this.videoPlayer_.height;
44 | adsRequest.nonLinearAdSlotWidth = this.videoPlayer_.width;
45 | adsRequest.nonLinearAdSlotHeight = this.videoPlayer_.height;
46 | this.adsLoader_.requestAds(adsRequest);
47 | };
48 |
49 | Ads.prototype.pause = function() {
50 | if (this.adsManager_) {
51 | this.adsManager_.pause();
52 | }
53 | };
54 |
55 | Ads.prototype.resume = function() {
56 | if (this.adsManager_) {
57 | this.adsManager_.resume();
58 | }
59 | };
60 |
61 | Ads.prototype.resize = function(width, height) {
62 | if (this.adsManager_) {
63 | this.adsManager_.resize(width, height);
64 | }
65 | };
66 |
67 | Ads.prototype.contentCompleted = function() {
68 | this.contentCompleteCalled = true;
69 | this.adsLoader_.contentComplete();
70 | };
71 |
72 | /**
73 | * If we're playing post-rolls, ALL_ADS_COMPLETED will not have fired at this
74 | * point. Here the cotent video is done, so if ads are also done, we start the
75 | * next video.
76 | */
77 | Ads.prototype.contentEnded = function() {
78 | this.contentCompleted();
79 | if (this.allAdsCompleted) {
80 | this.application_.switchButtonToReplay();
81 | }
82 | };
83 |
84 | Ads.prototype.destroyAdsManager = function() {
85 | if (this.adsManager_) {
86 | this.adsManager_.destroy();
87 | this.adsManager_ = null;
88 | }
89 | };
90 |
91 | Ads.prototype.onAdsManagerLoaded_ = function(adsManagerLoadedEvent) {
92 | this.application_.log('Ads loaded.');
93 | const adsRenderingSettings = new google.ima.AdsRenderingSettings();
94 | adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true;
95 | this.adsManager_ = adsManagerLoadedEvent.getAdsManager(
96 | this.videoPlayer_.contentPlayer, adsRenderingSettings);
97 | this.startAdsManager_(this.adsManager_);
98 | };
99 |
100 | Ads.prototype.startAdsManager_ = function(adsManager) {
101 | if (adsManager.isCustomClickTrackingUsed()) {
102 | this.customClickDiv_.style.display = 'table';
103 | }
104 | // Attach the pause/resume events.
105 | adsManager.addEventListener(
106 | google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED,
107 | this.onContentPauseRequested_, false, this);
108 | adsManager.addEventListener(
109 | google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED,
110 | this.onContentResumeRequested_, false, this);
111 | // Handle errors.
112 | adsManager.addEventListener(
113 | google.ima.AdErrorEvent.Type.AD_ERROR, this.onAdError_, false, this);
114 | adsManager.addEventListener(
115 | google.ima.AdEvent.Type.ALL_ADS_COMPLETED, this.onAllAdsCompleted_, false,
116 | this);
117 | const events = [
118 | google.ima.AdEvent.Type.ALL_ADS_COMPLETED, google.ima.AdEvent.Type.CLICK,
119 | google.ima.AdEvent.Type.COMPLETE, google.ima.AdEvent.Type.FIRST_QUARTILE,
120 | google.ima.AdEvent.Type.LOADED, google.ima.AdEvent.Type.MIDPOINT,
121 | google.ima.AdEvent.Type.PAUSED, google.ima.AdEvent.Type.STARTED,
122 | google.ima.AdEvent.Type.THIRD_QUARTILE
123 | ];
124 | for (const index in events) {
125 | adsManager.addEventListener(events[index], this.onAdEvent_, false, this);
126 | }
127 |
128 | let initWidth;
129 | let initHeight;
130 | if (this.application_.fullscreen) {
131 | initWidth = this.application_.fullscreenWidth;
132 | initHeight = this.application_.fullscreenHeight;
133 | } else {
134 | initWidth = this.videoPlayer_.width;
135 | initHeight = this.videoPlayer_.height;
136 | }
137 | adsManager.init(initWidth, initHeight);
138 |
139 | adsManager.start();
140 | };
141 |
142 | Ads.prototype.onContentPauseRequested_ = function() {
143 | this.linearAdPlaying = true;
144 | this.application_.pauseForAd();
145 | this.application_.setVideoEndedCallbackEnabled(false);
146 | };
147 |
148 | Ads.prototype.onContentResumeRequested_ = function() {
149 | this.application_.setVideoEndedCallbackEnabled(true);
150 | this.linearAdPlaying = false;
151 | // Without this check the video starts over from the beginning on a
152 | // post-roll's CONTENT_RESUME_REQUESTED
153 | if (!this.contentCompleteCalled) {
154 | this.application_.resumeAfterAd();
155 | }
156 | };
157 |
158 | Ads.prototype.onAdEvent_ = function(adEvent) {
159 | this.application_.log('Ad event: ' + adEvent.type);
160 |
161 | if (adEvent.type == google.ima.AdEvent.Type.CLICK) {
162 | this.application_.adClicked();
163 | } else if (adEvent.type == google.ima.AdEvent.Type.LOADED) {
164 | const ad = adEvent.getAd();
165 | if (!ad.isLinear()) {
166 | this.onContentResumeRequested_();
167 | }
168 | }
169 | };
170 |
171 | Ads.prototype.onAdError_ = function(adErrorEvent) {
172 | this.application_.log('Ad error: ' + adErrorEvent.getError().toString());
173 | if (this.adsManager_) {
174 | this.adsManager_.destroy();
175 | }
176 | this.application_.resumeAfterAd();
177 | };
178 |
179 | /**
180 | * If we aren't playing post-rolls, ALL_ADS_COMPLETED will be fired before
181 | * the video player fires the ended event. Here ads are done, so if
182 | * the content video is done, we start the next video. If ads are done but the
183 | * content video is still playing, we just let it finish.
184 | * @private
185 | */
186 | Ads.prototype.onAllAdsCompleted_ = function() {
187 | this.allAdsCompleted = true;
188 | if (this.contentCompleteCalled) {
189 | this.application_.switchButtonToReplay();
190 | }
191 | };
192 |
--------------------------------------------------------------------------------
/playlist/application.js:
--------------------------------------------------------------------------------
1 | // Copyright 2013 Google Inc. All Rights Reserved.
2 | // You may study, modify, and use this example for any purpose.
3 | // Note that this example is provided "as is", WITHOUT WARRANTY
4 | // of any kind either expressed or implied.
5 |
6 | /**
7 | * Handles user interaction and creates the player and ads controllers.
8 | */
9 | const Application = function() {
10 | this.console_ = document.getElementById('console');
11 | this.playButton_ = document.getElementById('playpause');
12 | this.playButton_.addEventListener(
13 | 'click', this.bind_(this, this.onClick_), false);
14 | this.replayButton_ = document.getElementById('replay');
15 | this.replayButton_.addEventListener(
16 | 'click', this.bind_(this, this.onReplay_), false);
17 | this.fullscreenButton_ = document.getElementById('fullscreen');
18 | this.fullscreenButton_.addEventListener(
19 | 'click', this.bind_(this, this.onFullscreenClick_), false);
20 |
21 | const playlistDiv = document.getElementById('playlistDiv');
22 | const playlistItems = playlistDiv.childNodes;
23 | for (let i = 0; i < playlistItems.length; i++) {
24 | if (playlistItems[i].tagName == 'DIV') {
25 | playlistItems[i].addEventListener(
26 | 'click', this.bind_(this, this.onPlaylistItemClick_), false);
27 | }
28 | }
29 |
30 | this.fullscreenWidth = null;
31 | this.fullscreenHeight = null;
32 |
33 | const fullScreenEvents =
34 | ['fullscreenchange', 'mozfullscreenchange', 'webkitfullscreenchange'];
35 | for (let key in fullScreenEvents) {
36 | document.addEventListener(
37 | fullScreenEvents[key], this.bind_(this, this.onFullscreenChange_),
38 | false);
39 | }
40 |
41 | this.initialUserActionHappened_ = false;
42 | this.playing_ = false;
43 | this.adsActive_ = false;
44 | this.adsDone_ = false;
45 | this.fullscreen = false;
46 |
47 | this.videoPlayer_ = new VideoPlayer();
48 | this.ads_ = new Ads(this, this.videoPlayer_);
49 | this.adTagUrl_ = 'https://pubads.g.doubleclick.net/gampad/ads?' +
50 | 'iu=/21775744923/external/single_ad_samples&sz=640x480&' +
51 | 'cust_params=sample_ct%3Dlinear&ciu_szs=300x250%2C728x90&gdfp_req=1&' +
52 | 'output=vast&unviewed_position_start=1&env=vp&impl=s&correlator=';
53 |
54 | this.videoEndedCallback_ = this.bind_(this, this.onContentEnded_);
55 | this.setVideoEndedCallbackEnabled(true);
56 | };
57 |
58 | /**
59 | * Registers or removes video ended callback based on the 'enable' param.
60 | * @param {boolean} enable
61 | */
62 | Application.prototype.setVideoEndedCallbackEnabled = function(enable) {
63 | if (enable) {
64 | this.videoPlayer_.registerVideoEndedCallback(this.videoEndedCallback_);
65 | } else {
66 | this.videoPlayer_.removeVideoEndedCallback(this.videoEndedCallback_);
67 | }
68 | };
69 |
70 | /**
71 | * Changes the play button to the replay button.
72 | */
73 | Application.prototype.switchButtonToReplay = function() {
74 | this.playButton_.style.display = 'none';
75 | this.replayButton_.style.display = 'block';
76 | };
77 |
78 | /**
79 | * Logs messages to the console.
80 | * @param {string} message
81 | */
82 | Application.prototype.log = function(message) {
83 | console.log(message);
84 | this.console_.innerHTML = this.console_.innerHTML + ' ' + message;
85 | };
86 |
87 | /**
88 | * Handles resuming content following ads.
89 | */
90 | Application.prototype.resumeAfterAd = function() {
91 | this.videoPlayer_.play();
92 | this.adsActive_ = false;
93 | this.updateChrome_();
94 | };
95 |
96 | /**
97 | * Handles pausing content for ad breaks.
98 | */
99 | Application.prototype.pauseForAd = function() {
100 | this.adsActive_ = true;
101 | this.playing_ = true;
102 | this.videoPlayer_.pause();
103 | this.updateChrome_();
104 | };
105 |
106 | /**
107 | * Pauses video on ad clicks.
108 | */
109 | Application.prototype.adClicked = function() {
110 | this.playing_ = false;
111 | this.updateChrome_();
112 | };
113 |
114 | /**
115 | * Function binding helper function.
116 | * @param {!Object} thisObj object to bind function.
117 | * @param {!Function} fn function being bound to object.
118 | * @return {!Function} returns the bound function.
119 | */
120 | Application.prototype.bind_ = function(thisObj, fn) {
121 | return function() {
122 | fn.apply(thisObj, arguments);
123 | };
124 | };
125 |
126 | /**
127 | * Handles pausing the content or ads for ad clicks.
128 | */
129 | Application.prototype.onClick_ = function() {
130 | if (!this.adsDone_) {
131 | if (!this.initialUserActionHappened_) {
132 | // The user clicked/tapped - inform the ads controller that this code
133 | // is being run in a user action thread.
134 | this.ads_.initialUserAction();
135 | this.initialUserActionHappened_ = true;
136 | }
137 | // At the same time, initialize the content player as well.
138 | // When content is loaded, we'll issue the ad request to prevent it
139 | // from interfering with the initialization. See
140 | // https://developers.google.com/interactive-media-ads/docs/sdks/html5/v3/ads#iosvideo
141 | // for more information.
142 | this.videoPlayer_.preloadContent(this.bind_(this, this.loadAds_));
143 | this.adsDone_ = true;
144 | return;
145 | }
146 |
147 | if (this.adsActive_) {
148 | if (this.playing_) {
149 | this.ads_.pause();
150 | } else {
151 | this.ads_.resume();
152 | }
153 | } else {
154 | if (this.playing_) {
155 | this.videoPlayer_.pause();
156 | } else {
157 | this.videoPlayer_.play();
158 | }
159 | }
160 |
161 | this.playing_ = !this.playing_;
162 |
163 | this.updateChrome_();
164 | };
165 |
166 | /**
167 | * Handles replaying the content and ads.
168 | */
169 | Application.prototype.onReplay_ = function() {
170 | this.switchButtonToPlay_();
171 | this.videoPlayer_.preloadContent(this.bind_(this, this.loadAds_));
172 | this.adsDone_ = true;
173 | };
174 |
175 | /**
176 | * Switches replay button to play button.
177 | */
178 | Application.prototype.switchButtonToPlay_ = function() {
179 | this.replayButton_.style.display = 'none';
180 | this.playButton_.style.display = 'block';
181 | };
182 |
183 | /**
184 | * Handles making the video fullscreen, or renturning from fullscreen.
185 | */
186 | Application.prototype.onFullscreenClick_ = function() {
187 | if (this.fullscreen) {
188 | // The video is currently in fullscreen mode
189 | const cancelFullscreen = document.exitFullscreen ||
190 | document.exitFullScreen || document.webkitCancelFullScreen ||
191 | document.mozCancelFullScreen;
192 | if (cancelFullscreen) {
193 | cancelFullscreen.call(document);
194 | } else {
195 | this.onFullscreenChange_();
196 | }
197 | } else {
198 | // Try to enter fullscreen mode in the browser
199 | const requestFullscreen = document.documentElement.requestFullscreen ||
200 | document.documentElement.webkitRequestFullscreen ||
201 | document.documentElement.mozRequestFullscreen ||
202 | document.documentElement.requestFullScreen ||
203 | document.documentElement.webkitRequestFullScreen ||
204 | document.documentElement.mozRequestFullScreen;
205 | if (requestFullscreen) {
206 | this.fullscreenWidth = window.screen.width;
207 | this.fullscreenHeight = window.screen.height;
208 | requestFullscreen.call(document.documentElement);
209 | } else {
210 | this.fullscreenWidth = window.innerWidth;
211 | this.fullscreenHeight = window.innerHeight;
212 | this.onFullscreenChange_();
213 | }
214 | }
215 | requestFullscreen.call(document.documentElement);
216 | };
217 |
218 | /**
219 | * Handles updating the play button image.
220 | */
221 | Application.prototype.updateChrome_ = function() {
222 | if (this.playing_) {
223 | this.playButton_.textContent = 'II';
224 | } else {
225 | // Unicode play symbol.
226 | this.playButton_.textContent = String.fromCharCode(9654);
227 | }
228 | };
229 |
230 | /**
231 | * Removes the 'loadedmetadata' listener and makes the ad request.
232 | */
233 | Application.prototype.loadAds_ = function() {
234 | this.videoPlayer_.removePreloadListener();
235 | this.ads_.requestAds(this.adTagUrl_);
236 | };
237 |
238 | /**
239 | * Handles resizing ads and content during fullscreen button clicks.
240 | */
241 | Application.prototype.onFullscreenChange_ = function() {
242 | if (this.fullscreen) {
243 | // The user just exited fullscreen
244 | // Resize the ad container
245 | this.ads_.resize(this.videoPlayer_.width, this.videoPlayer_.height);
246 | // Return the video to its original size and position
247 | this.videoPlayer_.resize(
248 | 'relative', '', '', this.videoPlayer_.width, this.videoPlayer_.height);
249 | this.fullscreen = false;
250 | } else {
251 | // The fullscreen button was just clicked
252 | // Resize the ad container
253 | const width = this.fullscreenWidth;
254 | const height = this.fullscreenHeight;
255 | this.makeAdsFullscreen_();
256 | // Make the video take up the entire screen
257 | this.videoPlayer_.resize('absolute', 0, 0, width, height);
258 | this.fullscreen = true;
259 | }
260 | };
261 |
262 | /**
263 | * Resizes ads for fullscreen.
264 | */
265 | Application.prototype.makeAdsFullscreen_ = function() {
266 | this.ads_.resize(this.fullscreenWidth, this.fullscreenHeight);
267 | };
268 |
269 | /**
270 | * Makes call to update UI on content ending.
271 | */
272 | Application.prototype.onContentEnded_ = function() {
273 | this.ads_.contentEnded();
274 | };
275 |
276 | /**
277 | * Handles clicks on playlist items.
278 | * @param {!Object} event the click event.
279 | */
280 | Application.prototype.onPlaylistItemClick_ = function(event) {
281 | // Terms of Service says we can't kill an ad prematurely, so we will only
282 | // switch videos if there isn't an ad playing.
283 | if (!this.ads_.linearAdPlaying) {
284 | this.ads_.destroyAdsManager();
285 | this.ads_.contentCompleted();
286 | if (!this.initialUserActionHappened_) {
287 | this.ads_.initialUserAction();
288 | this.initialUserActionHappened_ = true;
289 | }
290 | this.adsDone_ = true;
291 | this.videoPlayer_.setContentVideoIndex(event.target.id);
292 | this.videoPlayer_.preloadContent(this.bind_(this, this.loadAds_));
293 | }
294 | };
295 |
--------------------------------------------------------------------------------
/playlist/img/android_preview.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/googleads/googleads-ima-html5/bc910dc76a00f46b81483c81ee057da696933799/playlist/img/android_preview.jpg
--------------------------------------------------------------------------------
/playlist/img/doubleclick_preview.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/googleads/googleads-ima-html5/bc910dc76a00f46b81483c81ee057da696933799/playlist/img/doubleclick_preview.jpg
--------------------------------------------------------------------------------
/playlist/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
25 |
26 |
27 |
38 |
39 | IMA HTML5 SDK Playlist Demo
40 |
41 |
42 |
43 | IMA HTML5 SDK Playlist Demo
44 |
45 |
46 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
Click here for more info on your ad.
58 |
59 |
60 |
62 |
63 |
69 |
70 |
71 |
72 |
73 |
74 | Video 1
75 |
76 |
77 |
78 | Video 2
79 |
80 |
81 |
82 |
83 | Welcome to IMA HTML5 SDK Demo!
84 |
85 |
86 |
87 |
88 |
95 |
96 |
97 |
--------------------------------------------------------------------------------
/playlist/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: arial, verdana, sans-serif;
3 | overflow: hidden;
4 | }
5 |
6 | header, footer { text-align:center; width: 100%; }
7 |
8 | header {
9 | height: 40px;
10 | font-size: 40px;
11 | font-weight: bold;
12 | margin-top: 20px;
13 | margin-bottom: 20px;
14 | }
15 |
16 | footer {
17 | margin-top: 20px;
18 | }
19 |
20 | #container {
21 | margin-left: auto;
22 | margin-right: auto;
23 | width: 728px;
24 | }
25 |
26 | #videoplayer {
27 | position: relative;
28 | background-color: #000;
29 | border-radius: 5px;
30 | box-shadow: 0px 0px 20px rgba(50, 50, 50, 0.95);
31 | border: 2px #ccc solid;
32 | width: 640px;
33 | height: 360px;
34 | margin-left: auto;
35 | margin-right: auto;
36 | margin-top: 20px;
37 | }
38 |
39 | #content-wrapper {
40 | position:relative;
41 | top: 0px;
42 | left: 0px
43 | }
44 |
45 | #playpause, #replay {
46 | position: absolute;
47 | left: 20px;
48 | bottom: 20px;
49 | height: 40px;
50 | width: 100px;
51 | border-style: none;
52 | font-weight: bold;
53 | font-size: 25px;
54 | opacity: 0.5;
55 | background-color: #fff;
56 | border-radius: 5px;
57 | border: 1px transparent solid;
58 | color: #000;
59 | cursor: pointer;
60 | line-height: 0;
61 | }
62 |
63 | #replay {
64 | display: none;
65 | }
66 |
67 | #playpause:hover, #replay:hover {
68 | border: 1px #f00 solid;
69 | color: #f00;
70 | }
71 |
72 | #fullscreen {
73 | position: absolute;
74 | bottom: 20px;
75 | left: 140px;
76 | height: 40px;
77 | width: 100px;
78 | border-style: none;
79 | font-weight: bold;
80 | font-size: 25px;
81 | opacity: 0.5;
82 | background-color: #fff;
83 | border-radius: 5px;
84 | border: 1px transparent solid;
85 | color: #000;
86 | cursor: pointer;
87 | line-height: 0;
88 | }
89 |
90 | #fullscreen:hover {
91 | border: 1px #f00 solid;
92 | color: #f00;
93 | }
94 |
95 | #content {
96 | overflow: hidden;
97 | }
98 |
99 | #content, #adcontainer {
100 | position: absolute;
101 | top: 0px;
102 | left: 0px;
103 | width: 640px;
104 | height: 360px;
105 | }
106 |
107 | #playlistDiv {
108 | height: 122px;
109 | width: 224px;
110 | margin-left: auto;
111 | margin-right: auto;
112 | margin-top: 10px;
113 | margin-bottom: 10px;
114 | text-align: center;
115 | }
116 |
117 | .playlistItem {
118 | height: 122px;
119 | width: 102px;
120 | float: left;
121 | margin-right: 10px;
122 | cursor: pointer;
123 | }
124 |
125 | .playlistImg {
126 | border: 1px solid;
127 | }
128 |
129 | #console {
130 | font-family: courier, monospace;
131 | font-size: 12px;
132 | margin-top: 20px;
133 | height: 200px;
134 | width: 630px;
135 | padding: 5px;
136 | border: 1px #ccc solid;
137 | overflow-y: scroll;
138 | margin-left: auto;
139 | margin-right: auto;
140 | }
141 |
142 | #companionDiv, #customClick {
143 | width: 728px;
144 | height: 90px;
145 | margin-top: 20px;
146 | }
147 |
148 | #customClick {
149 | background-color: #807F80;
150 | display: none;
151 | text-align: center;
152 | }
153 |
154 | #customClickTextWrapper {
155 | display: table-cell;
156 | vertical-align: middle;
157 | }
158 |
--------------------------------------------------------------------------------
/playlist/video_player.js:
--------------------------------------------------------------------------------
1 | // Copyright 2013 Google Inc. All Rights Reserved.
2 | // You may study, modify, and use this example for any purpose.
3 | // Note that this example is provided "as is", WITHOUT WARRANTY
4 | // of any kind either expressed or implied.
5 |
6 | /**
7 | * Handles video player functionality.
8 | */
9 | const VideoPlayer = function() {
10 | this.contentPlayer = document.getElementById('content');
11 | this.adContainer = document.getElementById('adcontainer');
12 | this.videoPlayerContainer_ = document.getElementById('videoplayer');
13 |
14 | this.contentIndex = 0;
15 | this.contentUrls = [
16 | 'https://storage.googleapis.com/gvabox/media/samples/stock.mp4',
17 | 'https://storage.googleapis.com/gvabox/media/samples/android.mp4'
18 | ];
19 |
20 | this.width = 640;
21 | this.height = 360;
22 | };
23 |
24 | VideoPlayer.prototype.preloadContent = function(contentLoadedAction) {
25 | // If this is the initial user action on iOS or Android device,
26 | // simulate playback to enable the video element for later program-triggered
27 | // playback.
28 | if (this.isMobilePlatform()) {
29 | this.preloadListener_ = contentLoadedAction;
30 | this.contentPlayer.addEventListener(
31 | 'loadedmetadata', contentLoadedAction, false);
32 | this.setContentVideoSource_(this.contentIndex);
33 | } else {
34 | this.setContentVideoSource_(this.contentIndex);
35 | contentLoadedAction();
36 | }
37 | };
38 |
39 | VideoPlayer.prototype.removePreloadListener = function() {
40 | if (this.preloadListener_) {
41 | this.contentPlayer.removeEventListener(
42 | 'loadedmetadata', this.preloadListener_, false);
43 | this.preloadListener_ = null;
44 | }
45 | };
46 |
47 | VideoPlayer.prototype.play = function() {
48 | this.contentPlayer.play();
49 | };
50 |
51 | VideoPlayer.prototype.pause = function() {
52 | this.contentPlayer.pause();
53 | };
54 |
55 | VideoPlayer.prototype.isMobilePlatform = function() {
56 | return this.contentPlayer.paused &&
57 | (navigator.userAgent.match(/(iPod|iPhone|iPad)/) ||
58 | navigator.userAgent.toLowerCase().indexOf('android') > -1);
59 | };
60 |
61 | VideoPlayer.prototype.resize = function(position, top, left, width, height) {
62 | this.videoPlayerContainer_.style.position = position;
63 | this.videoPlayerContainer_.style.top = top + 'px';
64 | this.videoPlayerContainer_.style.left = left + 'px';
65 | this.videoPlayerContainer_.style.width = width + 'px';
66 | this.videoPlayerContainer_.style.height = height + 'px';
67 | this.contentPlayer.style.width = width + 'px';
68 | this.contentPlayer.style.height = height + 'px';
69 | };
70 |
71 | VideoPlayer.prototype.registerVideoEndedCallback = function(callback) {
72 | this.contentPlayer.addEventListener('ended', callback, false);
73 | };
74 |
75 | VideoPlayer.prototype.removeVideoEndedCallback = function(callback) {
76 | this.contentPlayer.removeEventListener('ended', callback, false);
77 | };
78 |
79 | VideoPlayer.prototype.setContentVideoIndex = function(index) {
80 | this.contentIndex = index;
81 | };
82 |
83 | VideoPlayer.prototype.setContentVideoSource_ = function(index) {
84 | this.contentIndex = index;
85 | this.contentPlayer.src = this.contentUrls[index];
86 | this.contentPlayer.load();
87 | };
88 |
--------------------------------------------------------------------------------
/simple/ads.js:
--------------------------------------------------------------------------------
1 | // Copyright 2013 Google Inc. All Rights Reserved.
2 | // You may study, modify, and use this example for any purpose.
3 | // Note that this example is provided "as is", WITHOUT WARRANTY
4 | // of any kind either expressed or implied.
5 |
6 | // [START init_player]
7 | let adsManager;
8 | let adsLoader;
9 | let adDisplayContainer;
10 | let isAdPlaying;
11 | let isContentFinished;
12 | let playButton;
13 | let videoContent;
14 | let adContainer;
15 |
16 | // On window load, attach an event to the play button click
17 | // that triggers playback of the video element.
18 | window.addEventListener('load', function(event) {
19 | videoContent = document.getElementById('contentElement');
20 | adContainer = document.getElementById('adContainer');
21 | adContainer.addEventListener('click', adContainerClick);
22 | playButton = document.getElementById('playButton');
23 | playButton.addEventListener('click', playAds);
24 | setUpIMA();
25 | });
26 | // [END init_player]
27 |
28 | // [START ima_setup]
29 | /**
30 | * Sets up IMA ad display container, ads loader, and makes an ad request.
31 | */
32 | function setUpIMA() {
33 | // Create the ad display container.
34 | createAdDisplayContainer();
35 | // Create ads loader.
36 | adsLoader = new google.ima.AdsLoader(adDisplayContainer);
37 | // Listen and respond to ads loaded and error events.
38 | adsLoader.addEventListener(
39 | google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED,
40 | onAdsManagerLoaded, false);
41 | adsLoader.addEventListener(
42 | google.ima.AdErrorEvent.Type.AD_ERROR, onAdError, false);
43 |
44 | // An event listener to tell the SDK that our content video
45 | // is completed so the SDK can play any post-roll ads.
46 | const contentEndedListener = function() {
47 | // An ad might have been playing in the content element, in which case the
48 | // content has not actually ended.
49 | if (isAdPlaying) return;
50 | isContentFinished = true;
51 | adsLoader.contentComplete();
52 | };
53 | videoContent.onended = contentEndedListener;
54 |
55 | // Request video ads.
56 | const adsRequest = new google.ima.AdsRequest();
57 | adsRequest.adTagUrl = 'https://pubads.g.doubleclick.net/gampad/ads?' +
58 | 'iu=/21775744923/external/single_ad_samples&sz=640x480&' +
59 | 'cust_params=sample_ct%3Dlinear&ciu_szs=300x250%2C728x90&gdfp_req=1&' +
60 | 'output=vast&unviewed_position_start=1&env=vp&impl=s&correlator=';
61 |
62 | // Specify the linear and nonlinear slot sizes. This helps the SDK to
63 | // select the correct creative if multiple are returned.
64 | adsRequest.linearAdSlotWidth = 640;
65 | adsRequest.linearAdSlotHeight = 400;
66 |
67 | adsRequest.nonLinearAdSlotWidth = 640;
68 | adsRequest.nonLinearAdSlotHeight = 150;
69 |
70 | adsLoader.requestAds(adsRequest);
71 | }
72 | // [END ima_setup]
73 |
74 | // [START create_ad_display_container]
75 | /**
76 | * Sets the 'adContainer' div as the IMA ad display container.
77 | */
78 | function createAdDisplayContainer() {
79 | adDisplayContainer = new google.ima.AdDisplayContainer(
80 | document.getElementById('adContainer'), videoContent);
81 | }
82 | // [END create_ad_display_container]
83 |
84 | // [START play_ads]
85 | /**
86 | * Loads the video content and initializes IMA ad playback.
87 | */
88 | function playAds() {
89 | // Initialize the container. Must be done through a user action on mobile
90 | // devices.
91 | videoContent.load();
92 | adDisplayContainer.initialize();
93 |
94 | try {
95 | // Initialize the ads manager. This call starts ad playback for VMAP ads.
96 | adsManager.init(640, 360);
97 | // Call play to start showing the ad. Single video and overlay ads will
98 | // start at this time; the call will be ignored for VMAP ads.
99 | adsManager.start();
100 | } catch (adError) {
101 | // An error may be thrown if there was a problem with the VAST response.
102 | videoContent.play();
103 | }
104 | }
105 | // [END play_ads]
106 |
107 | // [START ads_loader_events]
108 | /**
109 | * Handles the ad manager loading and sets ad event listeners.
110 | * @param {!google.ima.AdsManagerLoadedEvent} adsManagerLoadedEvent
111 | */
112 | function onAdsManagerLoaded(adsManagerLoadedEvent) {
113 | // Get the ads manager.
114 | const adsRenderingSettings = new google.ima.AdsRenderingSettings();
115 | adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true;
116 | // videoContent should be set to the content video element.
117 | adsManager =
118 | adsManagerLoadedEvent.getAdsManager(videoContent, adsRenderingSettings);
119 |
120 | // Add listeners to the required events.
121 | // [START ads_manager_error_handler]
122 | adsManager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, onAdError);
123 | // [END ads_manager_error_handler]
124 | // [START ads_manager_play_pause_handlers]
125 | adsManager.addEventListener(
126 | google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED, onContentPauseRequested);
127 | adsManager.addEventListener(
128 | google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED,
129 | onContentResumeRequested);
130 | // [END ads_manager_play_pause_handlers]
131 | // [START ads_manager_loaded_handler]
132 | adsManager.addEventListener(google.ima.AdEvent.Type.LOADED, onAdLoaded);
133 | // [END ads_manager_loaded_handler]
134 | }
135 |
136 | /**
137 | * Handles ad errors.
138 | * @param {!google.ima.AdErrorEvent} adErrorEvent
139 | */
140 | function onAdError(adErrorEvent) {
141 | // Handle the error logging.
142 | console.log(adErrorEvent.getError());
143 | adsManager.destroy();
144 | }
145 | // [END ads_loader_events]
146 |
147 | // [START ad_container_click]
148 | /**
149 | * Handles clicks on the ad container to support expected play and pause
150 | * behavior on mobile devices.
151 | * @param {!Event} event
152 | */
153 | function adContainerClick(event) {
154 | console.log("ad container clicked");
155 | if(videoContent.paused) {
156 | videoContent.play();
157 | } else {
158 | videoContent.pause();
159 | }
160 | }
161 | // [END ad_container_click]
162 |
163 | // [START play_pause_responses]
164 | /**
165 | * Pauses video content and sets up ad UI.
166 | */
167 | function onContentPauseRequested() {
168 | isAdPlaying = true;
169 | videoContent.pause();
170 | // This function is where you should setup UI for showing ads (for example,
171 | // display ad timer countdown, disable seeking and more.)
172 | // setupUIForAds();
173 | }
174 |
175 | /**
176 | * Resumes video content and removes ad UI.
177 | */
178 | function onContentResumeRequested() {
179 | isAdPlaying = false;
180 | if (!isContentFinished) {
181 | videoContent.play();
182 | }
183 | // This function is where you should ensure that your UI is ready
184 | // to play content. It is the responsibility of the Publisher to
185 | // implement this function when necessary.
186 | // setupUIForContent();
187 | }
188 | // [END play_pause_responses]
189 |
190 | // [START ad_loaded_handler]
191 | /**
192 | * Handles ad loaded event to support non-linear ads. Continues content playback
193 | * if the ad is not linear.
194 | * @param {!google.ima.AdEvent} adEvent
195 | */
196 | function onAdLoaded(adEvent) {
197 | let ad = adEvent.getAd();
198 | if (!ad.isLinear()) {
199 | videoContent.play();
200 | }
201 | }
202 | // [END ad_loaded_handler]
203 |
204 | // [START resize_handler]
205 | window.addEventListener('resize', function(event) {
206 | console.log("window resized");
207 | if(adsManager) {
208 | let width = videoContent.clientWidth;
209 | let height = videoContent.clientHeight;
210 | adsManager.resize(width, height, google.ima.ViewMode.NORMAL);
211 | }
212 | });
213 | // [END resize_handler]
214 |
--------------------------------------------------------------------------------
/simple/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | IMA HTML5 Simple Demo
4 |
5 |
6 |
7 |
8 |
9 |
10 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/simple/style.css:
--------------------------------------------------------------------------------
1 | /* [START player_css] */
2 | #mainContainer {
3 | position: relative;
4 | width: 640px;
5 | height: 360px;
6 | }
7 |
8 | #content {
9 | position: absolute;
10 | top: 0;
11 | left: 0;
12 | width: 640px;
13 | height: 360px;
14 | }
15 |
16 | #contentElement {
17 | width: 640px;
18 | height: 360px;
19 | overflow: hidden;
20 | }
21 |
22 | #playButton {
23 | margin-top:10px;
24 | vertical-align: top;
25 | width: 350px;
26 | height: 60px;
27 | padding: 0;
28 | font-size: 22px;
29 | color: white;
30 | text-align: center;
31 | text-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
32 | background: #2c3e50;
33 | border: 0;
34 | border-bottom: 2px solid #22303f;
35 | cursor: pointer;
36 | -webkit-box-shadow: inset 0 -2px #22303f;
37 | box-shadow: inset 0 -2px #22303f;
38 | }
39 | /* [END player_css] */
40 |
41 | /* [START ad_container_css] */
42 | #adContainer {
43 | position: absolute;
44 | top: 0;
45 | left: 0;
46 | width: 640px;
47 | height: 360px;
48 | }
49 | /* [END ad_container_css] */
50 |
--------------------------------------------------------------------------------
/vpaid/linear/VpaidVideoAd.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview A sample VPAID ad useful for testing a VPAID JS enabled player.
3 | * This ad will just play a video.
4 | */
5 |
6 | /**
7 | * @constructor
8 | */
9 | const VpaidVideoPlayer = function() {
10 | /**
11 | * The slot is the div element on the main page that the ad is supposed to
12 | * occupy.
13 | * @type {Object}
14 | * @private
15 | */
16 | this.slot_ = null;
17 |
18 | /**
19 | * The video slot is the video element used by the ad to render video content.
20 | * @type {Object}
21 | * @private
22 | */
23 | this.videoSlot_ = null;
24 |
25 | /**
26 | * An object containing all registered events. These events are all
27 | * callbacks for use by the VPAID ad.
28 | * @type {Object}
29 | * @private
30 | */
31 | this.eventsCallbacks_ = {};
32 |
33 | /**
34 | * A list of getable and setable attributes.
35 | * @type {Object}
36 | * @private
37 | */
38 | this.attributes_ = {
39 | 'companions': '',
40 | 'desiredBitrate': 256,
41 | 'duration': 10,
42 | 'expanded': false,
43 | 'height': 0,
44 | 'icons': '',
45 | 'linear': true,
46 | 'remainingTime': 10,
47 | 'skippableState': false,
48 | 'viewMode': 'normal',
49 | 'width': 0,
50 | 'volume': 1.0
51 | };
52 |
53 | /**
54 | * A set of ad playback events to be reported.
55 | * @type {Object}
56 | * @private
57 | */
58 | this.quartileEvents_ = [
59 | {event: 'AdImpression', value: 0}, {event: 'AdVideoStart', value: 0},
60 | {event: 'AdVideoFirstQuartile', value: 25},
61 | {event: 'AdVideoMidpoint', value: 50},
62 | {event: 'AdVideoThirdQuartile', value: 75},
63 | {event: 'AdVideoComplete', value: 100}
64 | ];
65 |
66 | /**
67 | * @type {number} An index into what quartile was last reported.
68 | * @private
69 | */
70 | this.nextQuartileIndex_ = 0;
71 |
72 | /**
73 | * Parameters passed in from the AdParameters section of the VAST.
74 | * Used for video URL and MIME type.
75 | * @type {!object}
76 | * @private
77 | */
78 | this.parameters_ = {};
79 | };
80 |
81 | /**
82 | * Returns the supported VPAID verion.
83 | * @param {string} version
84 | * @return {string}
85 | */
86 | VpaidVideoPlayer.prototype.handshakeVersion = function(version) {
87 | return ('2.0');
88 | };
89 |
90 | /**
91 | * Initializes all attributes in the ad. The ad will not start until startAd is\
92 | * called.
93 | * @param {number} width The ad width.
94 | * @param {number} height The ad height.
95 | * @param {string} viewMode The ad view mode.
96 | * @param {number} desiredBitrate The chosen bitrate.
97 | * @param {Object} creativeData Data associated with the creative.
98 | * @param {Object} environmentVars Runtime variables associated with the
99 | * creative like the slot and video slot.
100 | */
101 | VpaidVideoPlayer.prototype.initAd = function(
102 | width, height, viewMode, desiredBitrate, creativeData, environmentVars) {
103 | this.attributes_['width'] = width;
104 | this.attributes_['height'] = height;
105 | this.attributes_['viewMode'] = viewMode;
106 | this.attributes_['desiredBitrate'] = desiredBitrate;
107 |
108 | // slot and videoSlot are passed as part of the environmentVars
109 | this.slot_ = environmentVars.slot;
110 | this.videoSlot_ = environmentVars.videoSlot;
111 |
112 | // Parse the incoming ad parameters.
113 | this.parameters_ = JSON.parse(creativeData['AdParameters']);
114 |
115 | this.log(
116 | 'initAd ' + width + 'x' + height + ' ' + viewMode + ' ' + desiredBitrate);
117 | this.updateVideoSlot_();
118 | this.videoSlot_.addEventListener(
119 | 'timeupdate', this.timeUpdateHandler_.bind(this), false);
120 | this.videoSlot_.addEventListener(
121 | 'loadedmetadata', this.loadedMetadata_.bind(this), false);
122 | this.videoSlot_.addEventListener('ended', this.stopAd.bind(this), false);
123 | this.slot_.addEventListener('click', this.clickAd_.bind(this), false);
124 | this.callEvent_('AdLoaded');
125 | };
126 |
127 | /**
128 | * Called when the ad is clicked.
129 | * @private
130 | */
131 | VpaidVideoPlayer.prototype.clickAd_ = function() {
132 | if ('AdClickThru' in this.eventsCallbacks_) {
133 | this.eventsCallbacks_['AdClickThru']('', '0', true);
134 | }
135 | };
136 |
137 | /**
138 | * Called by the video element when video metadata is loaded.
139 | * @private
140 | */
141 | VpaidVideoPlayer.prototype.loadedMetadata_ = function() {
142 | // The ad duration is not known until the media metadata is loaded.
143 | // Then, update the player with the duration change.
144 | this.attributes_['duration'] = this.videoSlot_.duration;
145 | this.callEvent_('AdDurationChange');
146 | };
147 |
148 | /**
149 | * Called by the video element when the video reaches specific points during
150 | * playback.
151 | * @private
152 | */
153 | VpaidVideoPlayer.prototype.timeUpdateHandler_ = function() {
154 | if (this.nextQuartileIndex_ >= this.quartileEvents_.length) {
155 | return;
156 | }
157 | const percentPlayed =
158 | this.videoSlot_.currentTime * 100.0 / this.videoSlot_.duration;
159 | if (percentPlayed >= this.quartileEvents_[this.nextQuartileIndex_].value) {
160 | const lastQuartileEvent =
161 | this.quartileEvents_[this.nextQuartileIndex_].event;
162 | this.eventsCallbacks_[lastQuartileEvent]();
163 | this.nextQuartileIndex_ += 1;
164 | }
165 | if (this.videoSlot_.duration > 0) {
166 | this.attributes_['remainingTime'] =
167 | this.videoSlot_.duration - this.videoSlot_.currentTime;
168 | }
169 | };
170 |
171 | /**
172 | * Creates or updates the video slot and fills it with a supported video.
173 | * @private
174 | */
175 | VpaidVideoPlayer.prototype.updateVideoSlot_ = function() {
176 | if (this.videoSlot_ == null) {
177 | this.videoSlot_ = document.createElement('video');
178 | this.log('Warning: No video element passed to ad, creating element.');
179 | this.slot_.appendChild(this.videoSlot_);
180 | }
181 | this.updateVideoPlayerSize_();
182 | let foundSource = false;
183 | const videos = this.parameters_.videos || [];
184 | for (let i = 0; i < videos.length; i++) {
185 | // Choose the first video with a supported mimetype.
186 | if (this.videoSlot_.canPlayType(videos[i].mimetype) != '') {
187 | this.videoSlot_.setAttribute('src', videos[i].url);
188 | foundSource = true;
189 | break;
190 | }
191 | }
192 | if (!foundSource) {
193 | // Unable to find a source video.
194 | this.callEvent_('AdError');
195 | }
196 | };
197 |
198 | /**
199 | * Helper function to update the size of the video player.
200 | * @private
201 | */
202 | VpaidVideoPlayer.prototype.updateVideoPlayerSize_ = function() {
203 | this.videoSlot_.setAttribute('width', this.attributes_['width']);
204 | this.videoSlot_.setAttribute('height', this.attributes_['height']);
205 | };
206 |
207 | /**
208 | * Called by the wrapper to start the ad.
209 | */
210 | VpaidVideoPlayer.prototype.startAd = function() {
211 | this.log('Starting ad');
212 | this.videoSlot_.play();
213 |
214 | this.callEvent_('AdStarted');
215 | };
216 |
217 | /**
218 | * Called by the wrapper to stop the ad.
219 | */
220 | VpaidVideoPlayer.prototype.stopAd = function() {
221 | this.log('Stopping ad');
222 | // Calling AdStopped immediately terminates the ad. Setting a timeout allows
223 | // events to go through.
224 | const callback = this.callEvent_.bind(this);
225 | setTimeout(callback, 75, ['AdStopped']);
226 | };
227 |
228 | /**
229 | * Called when the video player changes the width/height of the container.
230 | * @param {number} width The new width.
231 | * @param {number} height A new height.
232 | * @param {string} viewMode A new view mode.
233 | */
234 | VpaidVideoPlayer.prototype.resizeAd = function(width, height, viewMode) {
235 | this.log('resizeAd ' + width + 'x' + height + ' ' + viewMode);
236 | this.attributes_['width'] = width;
237 | this.attributes_['height'] = height;
238 | this.attributes_['viewMode'] = viewMode;
239 | this.updateVideoPlayerSize_();
240 | this.callEvent_('AdSizeChange');
241 | };
242 |
243 | /**
244 | * Pauses the ad.
245 | */
246 | VpaidVideoPlayer.prototype.pauseAd = function() {
247 | this.log('pauseAd');
248 | this.videoSlot_.pause();
249 | this.callEvent_('AdPaused');
250 | };
251 |
252 | /**
253 | * Resumes the ad.
254 | */
255 | VpaidVideoPlayer.prototype.resumeAd = function() {
256 | this.log('resumeAd');
257 | this.videoSlot_.play();
258 | this.callEvent_('AdPlaying');
259 | };
260 |
261 | /**
262 | * Expands the ad.
263 | */
264 | VpaidVideoPlayer.prototype.expandAd = function() {
265 | this.log('expandAd');
266 | this.attributes_['expanded'] = true;
267 | this.callEvent_('AdExpanded');
268 | };
269 |
270 | /**
271 | * Collapses the ad.
272 | */
273 | VpaidVideoPlayer.prototype.collapseAd = function() {
274 | this.log('collapseAd');
275 | this.attributes_['expanded'] = false;
276 | };
277 |
278 | /**
279 | * Skips the ad.
280 | */
281 | VpaidVideoPlayer.prototype.skipAd = function() {
282 | this.log('skipAd');
283 | const skippableState = this.attributes_['skippableState'];
284 | if (skippableState) {
285 | this.callEvent_('AdSkipped');
286 | }
287 | };
288 |
289 | /**
290 | * Registers a callback for an event.
291 | * @param {Function} aCallback The callback function.
292 | * @param {string} eventName The callback type.
293 | * @param {Object} aContext The context for the callback.
294 | */
295 | VpaidVideoPlayer.prototype.subscribe = function(
296 | aCallback, eventName, aContext) {
297 | this.log('Subscribe ' + eventName);
298 | const callBack = aCallback.bind(aContext);
299 | this.eventsCallbacks_[eventName] = callBack;
300 | };
301 |
302 | /**
303 | * Removes a callback based on the eventName.
304 | * @param {string} eventName The callback type.
305 | */
306 | VpaidVideoPlayer.prototype.unsubscribe = function(eventName) {
307 | this.log('unsubscribe ' + eventName);
308 | this.eventsCallbacks_[eventName] = null;
309 | };
310 |
311 | /**
312 | * Returns whether the ad is linear.
313 | * @return {boolean} True if the ad is a linear, false for non linear.
314 | */
315 | VpaidVideoPlayer.prototype.getAdLinear = function() {
316 | return this.attributes_['linear'];
317 | };
318 |
319 | /**
320 | * Returns ad width.
321 | * @return {number} The ad width.
322 | */
323 | VpaidVideoPlayer.prototype.getAdWidth = function() {
324 | return this.attributes_['width'];
325 | };
326 |
327 | /**
328 | * Returns ad height.
329 | * @return {number} The ad height.
330 | */
331 | VpaidVideoPlayer.prototype.getAdHeight = function() {
332 | return this.attributes_['height'];
333 | };
334 |
335 | /**
336 | * Returns true if the ad is expanded.
337 | * @return {boolean}
338 | */
339 | VpaidVideoPlayer.prototype.getAdExpanded = function() {
340 | this.log('getAdExpanded');
341 | return this.attributes_['expanded'];
342 | };
343 |
344 | /**
345 | * Returns the skippable state of the ad.
346 | * @return {boolean}
347 | */
348 | VpaidVideoPlayer.prototype.getAdSkippableState = function() {
349 | this.log('getAdSkippableState');
350 | return this.attributes_['skippableState'];
351 | };
352 |
353 | /**
354 | * Returns the remaining ad time, in seconds.
355 | * @return {number} The time remaining in the ad.
356 | */
357 | VpaidVideoPlayer.prototype.getAdRemainingTime = function() {
358 | return this.attributes_['remainingTime'];
359 | };
360 |
361 | /**
362 | * Returns the duration of the ad, in seconds.
363 | * @return {number} The duration of the ad.
364 | */
365 | VpaidVideoPlayer.prototype.getAdDuration = function() {
366 | return this.attributes_['duration'];
367 | };
368 |
369 | /**
370 | * Returns the ad volume.
371 | * @return {number} The volume of the ad.
372 | */
373 | VpaidVideoPlayer.prototype.getAdVolume = function() {
374 | this.log('getAdVolume');
375 | return this.attributes_['volume'];
376 | };
377 |
378 | /**
379 | * Sets the ad volume.
380 | * @param {number} value The volume in percentage.
381 | */
382 | VpaidVideoPlayer.prototype.setAdVolume = function(value) {
383 | this.attributes_['volume'] = value;
384 | this.log('setAdVolume ' + value);
385 | this.callEvent_('AdVolumeChange');
386 | };
387 |
388 | /**
389 | * Returns a list of companion ads for the ad.
390 | * @return {string} List of companions in VAST XML.
391 | */
392 | VpaidVideoPlayer.prototype.getAdCompanions = function() {
393 | return this.attributes_['companions'];
394 | };
395 |
396 | /**
397 | * Returns a list of icons.
398 | * @return {string} A list of icons.
399 | */
400 | VpaidVideoPlayer.prototype.getAdIcons = function() {
401 | return this.attributes_['icons'];
402 | };
403 |
404 | /**
405 | * Logs events and messages.
406 | * @param {string} message
407 | */
408 | VpaidVideoPlayer.prototype.log = function(message) {
409 | console.log(message);
410 | };
411 |
412 | /**
413 | * Calls an event if there is a callback.
414 | * @param {string} eventType
415 | * @private
416 | */
417 | VpaidVideoPlayer.prototype.callEvent_ = function(eventType) {
418 | if (eventType in this.eventsCallbacks_) {
419 | this.eventsCallbacks_[eventType]();
420 | }
421 | };
422 |
423 | /**
424 | * Main function called by wrapper to get the VPAID ad.
425 | * @return {Object} The VPAID compliant ad.
426 | */
427 | var getVPAIDAd = function() {
428 | return new VpaidVideoPlayer();
429 | };
430 |
--------------------------------------------------------------------------------
/vpaid/nonlinear/VpaidNonLinear.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview A sample non-linear VPAID ad useful for testing a VPAID JS
3 | * enabled player. This ad will show a non-linear ad which can also enter linear
4 | * mode.
5 | */
6 |
7 | /** @unrestricted */
8 | const VpaidNonLinear = class {
9 | constructor() {
10 | /**
11 | * The slot is the div element on the main page that the ad is supposed to
12 | * occupy.
13 | * @private {Object}
14 | */
15 | this.slot_ = null;
16 |
17 | /**
18 | * The video slot is the video element used by the ad to render video
19 | * content.
20 | * @private {Object}
21 | */
22 | this.videoSlot_ = null;
23 |
24 | /**
25 | * An object containing all registered events. These events are all
26 | * callbacks for use by the VPAID ad.
27 | * @private {Object}
28 | */
29 | this.eventsCallbacks_ = {};
30 |
31 | /**
32 | * A list of getable and setable attributes.
33 | * @private {Object}
34 | */
35 | this.attributes_ = {
36 | 'companions': '',
37 | 'desiredBitrate': 256,
38 | 'duration': 10,
39 | 'expanded': false,
40 | 'height': 0,
41 | 'icons': '',
42 | 'linear': false,
43 | 'skippableState': false,
44 | 'viewMode': 'normal',
45 | 'width': 0,
46 | 'volume': 1.0
47 | };
48 |
49 | /**
50 | * When the ad was started.
51 | * @private {number}
52 | */
53 | this.startTime_ = 0;
54 |
55 | /**
56 | * A set of ad playback events to be reported.
57 | * @private {Object}
58 | */
59 | this.quartileEvents_ = [
60 | {event: 'AdImpression', value: 0}, {event: 'AdVideoStart', value: 0},
61 | {event: 'AdVideoFirstQuartile', value: 25},
62 | {event: 'AdVideoMidpoint', value: 50},
63 | {event: 'AdVideoThirdQuartile', value: 75},
64 | {event: 'AdVideoComplete', value: 100}
65 | ];
66 |
67 | /**
68 | * @private {number} An index into what quartile was last reported.
69 | */
70 | this.nextQuartileIndex_ = 0;
71 |
72 | /**
73 | * Parameters passed in from the AdParameters section of the VAST.
74 | * Used for video URL and MIME type.
75 | * @private {!Object}
76 | */
77 | this.parameters_ = {};
78 | }
79 |
80 | /**
81 | * Returns the supported VPAID verion.
82 | * @param {string} version
83 | * @return {string}
84 | */
85 | handshakeVersion(version) {
86 | return ('2.0');
87 | }
88 |
89 | /**
90 | * Initializes all attributes in the ad. The ad will not start until startAd
91 | * is called.
92 | * @param {number} width The ad width.
93 | * @param {number} height The ad height.
94 | * @param {string} viewMode The ad view mode.
95 | * @param {number} desiredBitrate The chosen bitrate.
96 | * @param {Object} creativeData Data associated with the creative.
97 | * @param {Object} environmentVars Runtime variables associated with the
98 | * creative like the slot and video slot.
99 | */
100 | initAd(
101 | width, height, viewMode, desiredBitrate, creativeData, environmentVars) {
102 | this.attributes_['width'] = width;
103 | this.attributes_['height'] = height;
104 | this.attributes_['viewMode'] = viewMode;
105 | this.attributes_['desiredBitrate'] = desiredBitrate;
106 |
107 | // slot and videoSlot are passed as part of the environmentVars
108 | this.slot_ = environmentVars.slot;
109 | this.videoSlot_ = environmentVars.videoSlot;
110 |
111 | // Parse the incoming ad parameters.
112 | this.parameters_ = JSON.parse(creativeData['AdParameters']);
113 |
114 | this.log(
115 | 'initAd ' + width + 'x' + height + ' ' + viewMode + ' ' +
116 | desiredBitrate);
117 | this.callEvent_('AdLoaded');
118 | }
119 |
120 | /**
121 | * Helper function to update the size of the video player.
122 | * @private
123 | */
124 | updateVideoPlayerSize_() {
125 | this.videoSlot_.setAttribute('width', this.attributes_['width']);
126 | this.videoSlot_.setAttribute('height', this.attributes_['height']);
127 | }
128 |
129 | /**
130 | * Called by the wrapper to start the ad.
131 | */
132 | startAd() {
133 | this.log('Starting ad');
134 |
135 | const date = new Date();
136 | this.startTime_ = date.getTime();
137 |
138 | // Create a div to contain our ad elements.
139 | const overlays = this.parameters_.overlays || [];
140 |
141 | const container = document.createElement('div');
142 | container.style.display = 'block';
143 | container.style.position = 'absolute';
144 | container.style.width = '100%';
145 | container.style.bottom = '0%';
146 | this.slot_.appendChild(container);
147 |
148 | // Create a div to serve as a button to go from a non-linear ad to linear.
149 | const linearButton = document.createElement('div');
150 | linearButton.style.background = 'green';
151 | linearButton.style.display = 'block';
152 | linearButton.style.margin = 'auto';
153 | linearButton.style.textAlign = 'center';
154 | linearButton.style.color = 'white';
155 | linearButton.style.width = '480px';
156 | linearButton.style.fontFamily = 'sans-serif';
157 | linearButton.textContent = 'Click here to switch to a linear ad';
158 | linearButton.addEventListener(
159 | 'click', this.linearButtonClick_.bind(this), false);
160 | container.appendChild(linearButton);
161 |
162 | // Create an img tag and populate it with the image passed in to the ad
163 | // parameters.
164 | const adImg = document.createElement('img');
165 | adImg.src = overlays[0] || '';
166 | adImg.style.margin = 'auto';
167 | adImg.style.display = 'block';
168 | adImg.addEventListener('click', this.adClick_.bind(this), false);
169 | container.appendChild(adImg);
170 |
171 | this.callEvent_('AdStarted');
172 | this.callEvent_('AdImpression');
173 | }
174 |
175 | /**
176 | * Called when the non-linear ad is clicked.
177 | * @private
178 | */
179 | adClick_() {
180 | if ('AdClickThru' in this.eventsCallbacks_) {
181 | this.eventsCallbacks_['AdClickThru']('', '0', true);
182 | }
183 | }
184 |
185 | /**
186 | * Called when the linear overlay is clicked. Plays the video passed in the
187 | * parameters.
188 | * @private
189 | */
190 | linearButtonClick_() {
191 | this.log('Linear Button Click');
192 | // This will turn the ad into a linear ad.
193 | this.attributes_.linear = true;
194 | this.callEvent_('AdLinearChange');
195 | // Remove all elements.
196 | while (this.slot_.firstChild) {
197 | this.slot_.removeChild(this.slot_.firstChild);
198 | }
199 |
200 | this.updateVideoPlayerSize_();
201 |
202 | // Start a video.
203 | const videos = this.parameters_.videos || [];
204 | for (let i = 0; i < videos.length; i++) {
205 | // Choose the first video with a supported mimetype.
206 | if (this.videoSlot_.canPlayType(videos[i].mimetype) != '') {
207 | this.videoSlot_.setAttribute('src', videos[i].url);
208 |
209 | // Set start time of linear ad to calculate remaining time.
210 | const date = new Date();
211 | this.startTime_ = date.getTime();
212 |
213 | this.videoSlot_.addEventListener(
214 | 'timeupdate', this.timeUpdateHandler_.bind(this), false);
215 | this.videoSlot_.addEventListener(
216 | 'loadedmetadata', this.loadedMetadata_.bind(this), false);
217 | this.videoSlot_.addEventListener(
218 | 'ended', this.stopAd.bind(this), false);
219 |
220 | this.videoSlot_.play();
221 |
222 | return;
223 | }
224 | }
225 | // Haven't found a video, so error.
226 | this.callEvent_('AdError');
227 | }
228 |
229 | /**
230 | * Called by the video element when the video reaches specific points during
231 | * playback.
232 | * @private
233 | */
234 | timeUpdateHandler_() {
235 | if (this.nextQuartileIndex_ >= this.quartileEvents_.length) {
236 | return;
237 | }
238 | const percentPlayed =
239 | this.videoSlot_.currentTime * 100.0 / this.videoSlot_.duration;
240 | let nextQuartile = this.quartileEvents_[this.nextQuartileIndex_];
241 | if (percentPlayed >= nextQuartile.value) {
242 | this.eventsCallbacks_[nextQuartile.event]();
243 | this.nextQuartileIndex_ += 1;
244 | }
245 | if (this.videoSlot_.duration > 0) {
246 | this.attributes_['remainingTime'] =
247 | this.videoSlot_.duration - this.videoSlot_.currentTime;
248 | }
249 | }
250 |
251 | /**
252 | * Called by the video element when video metadata is loaded.
253 | * @private
254 | */
255 | loadedMetadata_() {
256 | // The ad duration is not known until the media metadata is loaded.
257 | // Then, update the player with the duration change.
258 | this.attributes_['duration'] = this.videoSlot_.duration;
259 | this.callEvent_('AdDurationChange');
260 | }
261 |
262 | /**
263 | * Called by the wrapper to stop the ad.
264 | */
265 | stopAd() {
266 | this.log('Stopping ad');
267 | // Calling AdStopped immediately terminates the ad. Setting a timeout allows
268 | // events to go through.
269 | const callback = this.callEvent_.bind(this);
270 | setTimeout(callback, 75, ['AdStopped']);
271 | }
272 |
273 | /**
274 | * Called when the video player changes the width/height of the container.
275 | * @param {number} width The new width.
276 | * @param {number} height A new height.
277 | * @param {string} viewMode A new view mode.
278 | */
279 | resizeAd(width, height, viewMode) {
280 | this.log('resizeAd ' + width + 'x' + height + ' ' + viewMode);
281 | this.attributes_['width'] = width;
282 | this.attributes_['height'] = height;
283 | this.attributes_['viewMode'] = viewMode;
284 | this.updateVideoPlayerSize_();
285 | this.callEvent_('AdSizeChange');
286 | }
287 |
288 | /**
289 | * Pauses the ad.
290 | */
291 | pauseAd() {
292 | this.log('pauseAd');
293 | this.videoSlot_.pause();
294 | this.callEvent_('AdPaused');
295 | }
296 |
297 | /**
298 | * Resumes the ad.
299 | */
300 | resumeAd() {
301 | this.log('resumeAd');
302 | this.videoSlot_.play();
303 | this.callEvent_('AdPlaying');
304 | }
305 |
306 | /**
307 | * Expands the ad.
308 | */
309 | expandAd() {
310 | this.log('expandAd');
311 | this.attributes_['expanded'] = true;
312 | this.callEvent_('AdExpanded');
313 | }
314 |
315 | /**
316 | * Collapses the ad.
317 | */
318 | collapseAd() {
319 | this.log('collapseAd');
320 | this.attributes_['expanded'] = false;
321 | }
322 |
323 | /**
324 | * Skips the ad.
325 | */
326 | skipAd() {
327 | this.log('skipAd');
328 | if (this.attributes_['skippableState']) {
329 | this.callEvent_('AdSkipped');
330 | }
331 | }
332 |
333 | /**
334 | * Registers a callback for an event.
335 | * @param {Function} callback The callback function.
336 | * @param {string} eventName The callback type.
337 | * @param {Object} context The context for the callback.
338 | */
339 | subscribe(callback, eventName, context) {
340 | this.log('Subscribe ' + eventName);
341 | this.eventsCallbacks_[eventName] = callback.bind(context);
342 | }
343 |
344 | /**
345 | * Removes a callback based on the eventName.
346 | * @param {string} eventName The callback type.
347 | */
348 | unsubscribe(eventName) {
349 | this.log('unsubscribe ' + eventName);
350 | this.eventsCallbacks_[eventName] = null;
351 | }
352 |
353 | /**
354 | * Returns whether the ad is linear.
355 | * @return {boolean} True if the ad is a linear, false for non linear.
356 | */
357 | getAdLinear() {
358 | return this.attributes_['linear'];
359 | }
360 |
361 | /**
362 | * Returns ad width.
363 | * @return {number} The ad width.
364 | */
365 | getAdWidth() {
366 | return this.attributes_['width'];
367 | }
368 |
369 | /**
370 | * Returns ad height.
371 | * @return {number} The ad height.
372 | */
373 | getAdHeight() {
374 | return this.attributes_['height'];
375 | }
376 |
377 | /**
378 | * Returns true if the ad is expanded.
379 | * @return {boolean}
380 | */
381 | getAdExpanded() {
382 | this.log('getAdExpanded');
383 | return this.attributes_['expanded'];
384 | }
385 |
386 | /**
387 | * Returns the skippable state of the ad.
388 | * @return {boolean}
389 | */
390 | getAdSkippableState() {
391 | this.log('getAdSkippableState');
392 | return this.attributes_['skippableState'];
393 | }
394 |
395 | /**
396 | * Returns the remaining ad time, in seconds.
397 | * @return {number} The time remaining in the ad.
398 | */
399 | getAdRemainingTime() {
400 | return this.attributes_['remainingTime'];
401 | }
402 |
403 | /**
404 | * Returns the duration of the ad, in seconds.
405 | * @return {number} The duration of the ad.
406 | */
407 | getAdDuration() {
408 | return this.attributes_['duration'];
409 | }
410 |
411 | /**
412 | * Returns the ad volume.
413 | * @return {number} The volume of the ad.
414 | */
415 | getAdVolume() {
416 | this.log('getAdVolume');
417 | return this.attributes_['volume'];
418 | }
419 |
420 | /**
421 | * Sets the ad volume.
422 | * @param {number} value The volume in percentage.
423 | */
424 | setAdVolume(value) {
425 | this.attributes_['volume'] = value;
426 | this.log('setAdVolume ' + value);
427 | this.callEvent_('AdVolumeChange');
428 | }
429 |
430 | /**
431 | * Returns a list of companion ads for the ad.
432 | * @return {string} List of companions in VAST XML.
433 | */
434 | getAdCompanions() {
435 | return this.attributes_['companions'];
436 | }
437 |
438 | /**
439 | * Returns a list of icons.
440 | * @return {string} A list of icons.
441 | */
442 | getAdIcons() {
443 | return this.attributes_['icons'];
444 | }
445 |
446 | /**
447 | * Logs events and messages.
448 | * @param {string} message
449 | */
450 | log(message) {
451 | console.log(message);
452 | }
453 |
454 | /**
455 | * Calls an event if there is a callback.
456 | * @param {string} eventType
457 | * @private
458 | */
459 | callEvent_(eventType) {
460 | if (eventType in this.eventsCallbacks_) {
461 | this.eventsCallbacks_[eventType]();
462 | }
463 | }
464 | };
465 |
466 | /**
467 | * Main function called by wrapper to get the VPAID ad.
468 | * @return {Object} The VPAID compliant ad.
469 | */
470 | var getVPAIDAd = function() {
471 | return new VpaidNonLinear();
472 | };
473 |
--------------------------------------------------------------------------------