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