Source: ui/save_video_frame_button.js

  1. /*! @license
  2. * Shaka Player
  3. * Copyright 2016 Google LLC
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. goog.provide('shaka.ui.SaveVideoFrameButton');
  7. goog.require('shaka.ads.Utils');
  8. goog.require('shaka.ui.ContextMenu');
  9. goog.require('shaka.ui.Controls');
  10. goog.require('shaka.ui.Element');
  11. goog.require('shaka.ui.Enums');
  12. goog.require('shaka.ui.Locales');
  13. goog.require('shaka.ui.Localization');
  14. goog.require('shaka.ui.OverflowMenu');
  15. goog.require('shaka.ui.Utils');
  16. goog.require('shaka.util.Dom');
  17. /**
  18. * @extends {shaka.ui.Element}
  19. * @final
  20. * @export
  21. */
  22. shaka.ui.SaveVideoFrameButton = class extends shaka.ui.Element {
  23. /**
  24. * @param {!HTMLElement} parent
  25. * @param {!shaka.ui.Controls} controls
  26. */
  27. constructor(parent, controls) {
  28. super(parent, controls);
  29. const LocIds = shaka.ui.Locales.Ids;
  30. /** @private {!HTMLButtonElement} */
  31. this.button_ = shaka.util.Dom.createButton();
  32. this.button_.classList.add('shaka-save.video-frame-button');
  33. this.button_.classList.add('shaka-tooltip');
  34. /** @private {!HTMLElement} */
  35. this.icon_ = shaka.util.Dom.createHTMLElement('i');
  36. this.icon_.classList.add('material-icons-round');
  37. this.icon_.textContent = shaka.ui.Enums.MaterialDesignIcons.DOWNLOAD;
  38. this.button_.appendChild(this.icon_);
  39. const label = shaka.util.Dom.createHTMLElement('label');
  40. label.classList.add('shaka-overflow-button-label');
  41. label.classList.add('shaka-overflow-menu-only');
  42. this.nameSpan_ = shaka.util.Dom.createHTMLElement('span');
  43. this.nameSpan_.textContent =
  44. this.localization.resolve(LocIds.DOWNLOAD_VIDEO_FRAME);
  45. label.appendChild(this.nameSpan_);
  46. /** @private {!HTMLElement} */
  47. this.currentState_ = shaka.util.Dom.createHTMLElement('span');
  48. this.currentState_.classList.add('shaka-current-selection-span');
  49. label.appendChild(this.currentState_);
  50. this.button_.appendChild(label);
  51. this.updateLocalizedStrings_();
  52. this.parent.appendChild(this.button_);
  53. this.eventManager.listen(
  54. this.localization, shaka.ui.Localization.LOCALE_UPDATED, () => {
  55. this.updateLocalizedStrings_();
  56. });
  57. this.eventManager.listen(
  58. this.localization, shaka.ui.Localization.LOCALE_CHANGED, () => {
  59. this.updateLocalizedStrings_();
  60. });
  61. this.eventManager.listen(this.button_, 'click', () => {
  62. this.onClick_();
  63. });
  64. const vr = this.controls.getVR();
  65. this.eventManager.listen(vr, 'vrstatuschanged', () => {
  66. this.checkAvailability_();
  67. });
  68. this.eventManager.listen(
  69. this.adManager, shaka.ads.Utils.AD_STARTED, () => {
  70. this.checkAvailability_();
  71. });
  72. this.eventManager.listen(
  73. this.adManager, shaka.ads.Utils.AD_STOPPED, () => {
  74. this.checkAvailability_();
  75. });
  76. this.eventManager.listen(this.player, 'unloading', () => {
  77. this.checkAvailability_();
  78. });
  79. this.eventManager.listen(this.player, 'loaded', () => {
  80. this.checkAvailability_();
  81. });
  82. this.eventManager.listen(this.player, 'loaded', () => {
  83. this.checkAvailability_();
  84. });
  85. this.eventManager.listen(this.video, 'play', () => {
  86. this.checkAvailability_();
  87. });
  88. this.eventManager.listen(this.video, 'pause', () => {
  89. this.checkAvailability_();
  90. });
  91. this.eventManager.listen(this.video, 'seeking', () => {
  92. this.checkAvailability_();
  93. });
  94. this.checkAvailability_();
  95. }
  96. /** @private */
  97. onClick_() {
  98. const canvas = /** @type {!HTMLCanvasElement}*/ (
  99. document.createElement('canvas'));
  100. const context = /** @type {CanvasRenderingContext2D} */ (
  101. canvas.getContext('2d'));
  102. const video = /** @type {!HTMLVideoElement} */ (
  103. this.controls.getLocalVideo());
  104. canvas.width = video.videoWidth;
  105. canvas.height = video.videoHeight;
  106. context.drawImage(video, 0, 0, canvas.width, canvas.height);
  107. const dataURL = canvas.toDataURL('image/png');
  108. const downloadLink = /** @type {!HTMLAnchorElement}*/ (
  109. document.createElement('a'));
  110. downloadLink.href = dataURL;
  111. downloadLink.download =
  112. 'videoframe_' + video.currentTime.toFixed(3) + '.png';
  113. downloadLink.click();
  114. }
  115. /**
  116. * @private
  117. */
  118. checkAvailability_() {
  119. let available = true;
  120. if (this.controls.isPlayingVR()) {
  121. available = false;
  122. }
  123. if (this.player.drmInfo() || this.player.isAudioOnly()) {
  124. available = false;
  125. }
  126. if (this.ad) {
  127. available = false;
  128. }
  129. if (this.video.remote && this.video.remote.state != 'disconnected') {
  130. available = false;
  131. }
  132. shaka.ui.Utils.setDisplay(this.button_, available);
  133. }
  134. /**
  135. * @private
  136. */
  137. updateLocalizedStrings_() {
  138. const LocIds = shaka.ui.Locales.Ids;
  139. this.button_.ariaLabel =
  140. this.localization.resolve(LocIds.DOWNLOAD_VIDEO_FRAME);
  141. this.nameSpan_.textContent =
  142. this.localization.resolve(LocIds.DOWNLOAD_VIDEO_FRAME);
  143. }
  144. };
  145. /**
  146. * @implements {shaka.extern.IUIElement.Factory}
  147. * @final
  148. */
  149. shaka.ui.SaveVideoFrameButton.Factory = class {
  150. /** @override */
  151. create(rootElement, controls) {
  152. return new shaka.ui.SaveVideoFrameButton(rootElement, controls);
  153. }
  154. };
  155. shaka.ui.OverflowMenu.registerElement(
  156. 'save_video_frame', new shaka.ui.SaveVideoFrameButton.Factory());
  157. shaka.ui.ContextMenu.registerElement(
  158. 'save_video_frame', new shaka.ui.SaveVideoFrameButton.Factory());