video-element.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. JSMpeg.VideoElement = (function(){ "use strict";
  2. var VideoElement = function(element) {
  3. var url = element.dataset.url;
  4. if (!url) {
  5. throw ("VideoElement has no `data-url` attribute");
  6. }
  7. // Setup the div container, canvas and play button
  8. var addStyles = function(element, styles) {
  9. for (var name in styles) {
  10. element.style[name] = styles[name];
  11. }
  12. };
  13. this.container = element;
  14. addStyles(this.container, {
  15. display: 'inline-block',
  16. position: 'relative',
  17. minWidth: '80px', minHeight: '80px'
  18. });
  19. this.canvas = document.createElement('canvas');
  20. this.canvas.width = 960;
  21. this.canvas.height = 540;
  22. addStyles(this.canvas, {
  23. display: 'block',
  24. width: '100%'
  25. });
  26. this.container.appendChild(this.canvas);
  27. this.playButton = document.createElement('div');
  28. this.playButton.innerHTML = VideoElement.PLAY_BUTTON;
  29. addStyles(this.playButton, {
  30. zIndex: 2, position: 'absolute',
  31. top: '0', bottom: '0', left: '0', right: '0',
  32. maxWidth: '75px', maxHeight: '75px',
  33. margin: 'auto',
  34. opacity: '0.7',
  35. cursor: 'pointer'
  36. });
  37. this.container.appendChild(this.playButton);
  38. // Parse the data-options - we try to decode the values as json. This way
  39. // we can get proper boolean and number values. If JSON.parse() fails,
  40. // treat it as a string.
  41. var options = {canvas: this.canvas};
  42. for (var option in element.dataset) {
  43. try {
  44. options[option] = JSON.parse(element.dataset[option]);
  45. }
  46. catch(err) {
  47. options[option] = element.dataset[option];
  48. }
  49. }
  50. // Create the player instance
  51. this.player = new JSMpeg.Player(url, options);
  52. element.playerInstance = this.player;
  53. // Setup the poster element, if any
  54. if (options.poster && !options.autoplay && !this.player.options.streaming) {
  55. options.decodeFirstFrame = false;
  56. this.poster = new Image();
  57. this.poster.src = options.poster;
  58. this.poster.addEventListener('load', this.posterLoaded)
  59. addStyles(this.poster, {
  60. display: 'block', zIndex: 1, position: 'absolute',
  61. top: 0, left: 0, bottom: 0, right: 0
  62. });
  63. this.container.appendChild(this.poster);
  64. }
  65. // Add the click handler if this video is pausable
  66. if (!this.player.options.streaming) {
  67. this.container.addEventListener('click', this.onClick.bind(this));
  68. }
  69. // Hide the play button if this video immediately begins playing
  70. if (options.autoplay || this.player.options.streaming) {
  71. this.playButton.style.display = 'none';
  72. }
  73. // Set up the unlock audio buton for iOS devices. iOS only allows us to
  74. // play audio after a user action has initiated playing. For autoplay or
  75. // streaming players we set up a muted speaker icon as the button. For all
  76. // others, we can simply use the play button.
  77. if (this.player.audioOut && !this.player.audioOut.unlocked) {
  78. var unlockAudioElement = this.container;
  79. if (options.autoplay || this.player.options.streaming) {
  80. this.unmuteButton = document.createElement('div');
  81. this.unmuteButton.innerHTML = VideoElement.UNMUTE_BUTTON;
  82. addStyles(this.unmuteButton, {
  83. zIndex: 2, position: 'absolute',
  84. bottom: '10px', right: '20px',
  85. width: '75px', height: '75px',
  86. margin: 'auto',
  87. opacity: '0.7',
  88. cursor: 'pointer'
  89. });
  90. this.container.appendChild(this.unmuteButton);
  91. unlockAudioElement = this.unmuteButton;
  92. }
  93. this.unlockAudioBound = this.onUnlockAudio.bind(this, unlockAudioElement);
  94. unlockAudioElement.addEventListener('touchstart', this.unlockAudioBound, false);
  95. unlockAudioElement.addEventListener('click', this.unlockAudioBound, true);
  96. }
  97. };
  98. VideoElement.prototype.onUnlockAudio = function(element, ev) {
  99. if (this.unmuteButton) {
  100. ev.preventDefault();
  101. ev.stopPropagation();
  102. }
  103. this.player.audioOut.unlock(function(){
  104. if (this.unmuteButton) {
  105. this.unmuteButton.style.display = 'none';
  106. }
  107. element.removeEventListener('touchstart', this.unlockAudioBound);
  108. element.removeEventListener('click', this.unlockAudioBound);
  109. }.bind(this));
  110. };
  111. VideoElement.prototype.onClick = function(ev) {
  112. if (this.player.isPlaying) {
  113. this.player.pause();
  114. this.playButton.style.display = 'block';
  115. }
  116. else {
  117. this.player.play();
  118. this.playButton.style.display = 'none';
  119. if (this.poster) {
  120. this.poster.style.display = 'none';
  121. }
  122. }
  123. };
  124. VideoElement.PLAY_BUTTON =
  125. '<svg style="max-width: 75px; max-height: 75px;" ' +
  126. 'viewBox="0 0 200 200" alt="Play video">' +
  127. '<circle cx="100" cy="100" r="90" fill="none" '+
  128. 'stroke-width="15" stroke="#fff"/>' +
  129. '<polygon points="70, 55 70, 145 145, 100" fill="#fff"/>' +
  130. '</svg>';
  131. VideoElement.UNMUTE_BUTTON =
  132. '<svg style="max-width: 75px; max-height: 75px;" viewBox="0 0 75 75">' +
  133. '<polygon class="audio-speaker" stroke="none" fill="#fff" '+
  134. 'points="39,13 22,28 6,28 6,47 21,47 39,62 39,13"/>' +
  135. '<g stroke="#fff" stroke-width="5">' +
  136. '<path d="M 49,50 69,26"/>' +
  137. '<path d="M 69,50 49,26"/>' +
  138. '</g>' +
  139. '</svg>';
  140. return VideoElement;
  141. })();