Skip to content

Commit 56523e4

Browse files
feat: Add Watermark Support to Shaka Player UI (#7877)
This pull request introduces watermark support for the Shaka Player UI. Added functionality to overlay static or dynamic watermarks on videos. Included options to customize position, size, color, and opacity. Enhanced content security with dynamic watermarking capabilities. Resolves #7726 .
1 parent 6e4bfdf commit 56523e4

File tree

8 files changed

+464
-0
lines changed

8 files changed

+464
-0
lines changed

CONTRIBUTORS

+1
Original file line numberDiff line numberDiff line change
@@ -164,3 +164,4 @@ Koen Romers <[email protected]>
164164
Zhenghang Chen <[email protected]>
165165
Ashley Manners <[email protected]>
166166
Bidisha Das <[email protected]>
167+
Chafroud Tarek <[email protected]>

build/types/ui

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
+../../ui/ad_statistics_button.js
77
+../../ui/audio_language_selection.js
88
+../../ui/externs/ui.js
9+
+../../ui/externs/watermark.js
910
+../../ui/play_button.js
1011
+../../ui/big_play_button.js
1112
+../../ui/airplay_button.js
@@ -50,5 +51,6 @@
5051
+../../ui/vr_utils.js
5152
+../../ui/vr_webgl.js
5253

54+
+../../ui/watermark.js
5355
+../../ui/gl_matrix/matrix_4x4.js
5456
+../../ui/gl_matrix/matrix_quaternion.js

demo/config.js

+5
Original file line numberDiff line numberDiff line change
@@ -767,6 +767,11 @@ shakaDemo.Config = class {
767767
this.latestInput_.input().checked = true;
768768
}
769769

770+
this.addCustomTextInput_('Watermark text', (input) => {
771+
shakaDemoMain.setWatermarkText(input.value);
772+
});
773+
this.latestInput_.input().value = shakaDemoMain.getWatermarkText();
774+
770775
// shaka.log is not set if logging isn't enabled.
771776
// I.E. if using the release version of shaka.
772777
if (!shaka['log']) {

demo/main.js

+38
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ shakaDemo.Main = class {
6262
/** @private {boolean} */
6363
this.customContextMenu_ = false;
6464

65+
/** @private {string} */
66+
this.watermarkText_ = '';
67+
6568
/** @private {boolean} */
6669
this.nativeControlsEnabled_ = false;
6770

@@ -397,6 +400,11 @@ shakaDemo.Main = class {
397400
uiConfig.overflowMenuButtons.push('visualizer');
398401
}
399402
ui.configure(uiConfig);
403+
if (this.watermarkText_) {
404+
ui.setTextWatermark(this.watermarkText_);
405+
} else {
406+
ui.removeWatermark();
407+
}
400408
}
401409

402410
/** @private */
@@ -829,6 +837,27 @@ shakaDemo.Main = class {
829837
return this.customContextMenu_;
830838
}
831839

840+
/**
841+
* Set the text for watermark.
842+
*
843+
* @param {string} text
844+
*/
845+
setWatermarkText(text) {
846+
this.watermarkText_ = text;
847+
// Configure the UI, to add or remove the controls.
848+
this.configureUI_();
849+
this.remakeHash();
850+
}
851+
852+
/**
853+
* Get the current text for watermark.
854+
*
855+
* @return {string}
856+
*/
857+
getWatermarkText() {
858+
return this.watermarkText_;
859+
}
860+
832861
/**
833862
* Enable or disable the native controls.
834863
* Goes into effect during the next load.
@@ -1020,6 +1049,10 @@ shakaDemo.Main = class {
10201049
this.configureUI_();
10211050
}
10221051

1052+
if ('watermarkText' in params) {
1053+
this.watermarkText_ = params['watermarkText'];
1054+
}
1055+
10231056
if ('visualizer' in params) {
10241057
this.setIsVisualizerActive(true);
10251058
} else {
@@ -1601,6 +1634,10 @@ shakaDemo.Main = class {
16011634
params.push('customContextMenu');
16021635
}
16031636

1637+
if (this.watermarkText_) {
1638+
params.push('watermarkText=' + this.watermarkText_);
1639+
}
1640+
16041641
if (this.getIsVisualizerActive()) {
16051642
params.push('visualizer');
16061643
}
@@ -2038,3 +2075,4 @@ document.addEventListener('shaka-ui-load-failed', (event) => {
20382075
shakaDemoMain.initFailed(reasonCode);
20392076
});
20402077
});
2078+

test/test/util/fake_demo_main.js

+13
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,19 @@ shaka.test.FakeDemoMain = class {
106106
this.getAssetUnsupportedReason =
107107
jasmine.createSpy('getAssetUnsupportedReason');
108108
this.getAssetUnsupportedReason.and.returnValue(null);
109+
110+
/** @private {string} */
111+
this.watermarkText_ = '';
112+
113+
/** @type {!jasmine.Spy} */
114+
this.getWatermarkText = jasmine.createSpy('getWatermarkText');
115+
this.getWatermarkText.and.callFake(() => this.watermarkText_);
116+
117+
/** @type {!jasmine.Spy} */
118+
this.setWatermarkText = jasmine.createSpy('setWatermarkText');
119+
this.setWatermarkText.and.callFake((text) => {
120+
this.watermarkText_ = text;
121+
});
109122
}
110123

111124
/** Creates and assigns the mock demo main (and all of the real tab). */

ui/externs/watermark.js

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*! @license
2+
* Shaka Player
3+
* Copyright 2016 Google LLC
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
8+
/**
9+
* @externs
10+
* @suppress {duplicate} To prevent compiler errors with the namespace
11+
* being declared both here and by goog.provide in the library.
12+
*/
13+
14+
15+
/**
16+
* @typedef {{
17+
* type: string,
18+
* text: string,
19+
* position: string,
20+
* color: string,
21+
* size: number,
22+
* alpha: number,
23+
* interval: number,
24+
* skip: number,
25+
* displayDuration: number,
26+
* transitionDuration: number
27+
* }}
28+
*
29+
* @property {string} type
30+
* The type of watermark ('static' or 'dynamic').
31+
* Defaults to 'static'.
32+
* @property {string} text
33+
* The text content of the watermark. Required.
34+
* @property {string} position
35+
* Position of the watermark.
36+
* Defaults to 'top-left'.
37+
* @property {string} color
38+
* The color of the watermark text.
39+
* Defaults to 'white'.
40+
* @property {number} size
41+
* Font size of the watermark text in pixels.
42+
* Defaults to 24.
43+
* @property {number} alpha
44+
* Opacity of the watermark (0.0 to 1.0).
45+
* Defaults to 0.7.
46+
* @property {number} interval
47+
* Interval between position updates for dynamic watermarks (in seconds).
48+
* Only used when type is 'dynamic'.
49+
* Defaults to 2.
50+
* @property {number} skip
51+
* Skip duration for dynamic watermarks (in seconds).
52+
* Only used when type is 'dynamic'.
53+
* Defaults to 0.5.
54+
* @property {number} displayDuration
55+
* Duration to display watermark at each position (in seconds).
56+
* Only used when type is 'dynamic'.
57+
* Defaults to 2.
58+
* @property {number} transitionDuration
59+
* Duration of fade transitions between positions (in seconds).
60+
* Only used when type is 'dynamic'.
61+
* Defaults to 0.5.
62+
* @exportDoc
63+
*/
64+
shaka.ui.Watermark.Options;

ui/ui.js

+28
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ goog.require('shaka.Player');
1414
goog.require('shaka.log');
1515
goog.require('shaka.polyfill');
1616
goog.require('shaka.ui.Controls');
17+
goog.require('shaka.ui.Watermark');
1718
goog.require('shaka.util.ConfigUtils');
1819
goog.require('shaka.util.Dom');
1920
goog.require('shaka.util.FakeEvent');
@@ -66,6 +67,11 @@ shaka.ui.Overlay = class {
6667

6768
videoContainer['ui'] = this;
6869
video['ui'] = this;
70+
/** @private {shaka.ui.Watermark} */
71+
this.watermark_ = new shaka.ui.Watermark(
72+
this.videoContainer_,
73+
this.controls_,
74+
);
6975
}
7076

7177

@@ -83,6 +89,7 @@ shaka.ui.Overlay = class {
8389
await this.player_.destroy();
8490
}
8591
this.player_ = null;
92+
this.watermark_ = null;
8693
}
8794

8895

@@ -168,6 +175,27 @@ shaka.ui.Overlay = class {
168175
}
169176

170177

178+
/**
179+
* @param {string} text
180+
* @param {?shaka.ui.Watermark.Options=} options
181+
* @export
182+
*/
183+
setTextWatermark(text, options) {
184+
if (this.watermark_) {
185+
this.watermark_.setTextWatermark(text, options);
186+
}
187+
}
188+
189+
/**
190+
* @export
191+
*/
192+
removeWatermark() {
193+
if (this.watermark_) {
194+
this.watermark_.removeWatermark();
195+
}
196+
}
197+
198+
171199
/**
172200
* @return {!shaka.extern.UIConfiguration}
173201
* @private

0 commit comments

Comments
 (0)