Source: lib/cast/cast_utils.js

  1. /**
  2. * @license
  3. * Copyright 2016 Google Inc.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. goog.provide('shaka.cast.CastUtils');
  18. goog.require('shaka.util.FakeEvent');
  19. /**
  20. * @namespace shaka.cast.CastUtils
  21. * @summary A set of cast utility functions and variables shared between sender
  22. * and receiver.
  23. */
  24. /**
  25. * HTMLMediaElement events that are proxied while casting.
  26. * @const {!Array.<string>}
  27. */
  28. shaka.cast.CastUtils.VideoEvents = [
  29. 'ended',
  30. 'play',
  31. 'playing',
  32. 'pause',
  33. 'pausing',
  34. 'ratechange',
  35. 'seeked',
  36. 'seeking',
  37. 'timeupdate',
  38. 'volumechange'
  39. ];
  40. /**
  41. * HTMLMediaElement attributes that are proxied while casting.
  42. * @const {!Array.<string>}
  43. */
  44. shaka.cast.CastUtils.VideoAttributes = [
  45. 'buffered',
  46. 'currentTime',
  47. 'duration',
  48. 'ended',
  49. 'loop',
  50. 'muted',
  51. 'paused',
  52. 'playbackRate',
  53. 'seeking',
  54. 'videoHeight',
  55. 'videoWidth',
  56. 'volume'
  57. ];
  58. /**
  59. * HTMLMediaElement attributes that are transferred when casting begins.
  60. * @const {!Array.<string>}
  61. */
  62. shaka.cast.CastUtils.VideoInitStateAttributes = [
  63. 'loop',
  64. 'playbackRate'
  65. ];
  66. /**
  67. * HTMLMediaElement methods with no return value that are proxied while casting.
  68. * @const {!Array.<string>}
  69. */
  70. shaka.cast.CastUtils.VideoVoidMethods = [
  71. 'pause',
  72. 'play'
  73. ];
  74. /**
  75. * Player events that are proxied while casting.
  76. * @const {!Array.<string>}
  77. */
  78. shaka.cast.CastUtils.PlayerEvents = [
  79. 'adaptation',
  80. 'buffering',
  81. 'emsg',
  82. 'error',
  83. 'loading',
  84. 'unloading',
  85. 'texttrackvisibility',
  86. 'trackschanged'
  87. ];
  88. /**
  89. * Player getter methods that are proxied while casting.
  90. * @const {!Array.<string>}
  91. */
  92. shaka.cast.CastUtils.PlayerGetterMethods = [
  93. 'drmInfo',
  94. 'getConfiguration',
  95. 'getManifestUri',
  96. 'getPlaybackRate',
  97. 'getTracks',
  98. 'getStats',
  99. 'isBuffering',
  100. 'isInProgress',
  101. 'isLive',
  102. 'isTextTrackVisible',
  103. 'keySystem',
  104. 'seekRange'
  105. ];
  106. /**
  107. * Player getter and setter methods that are used to transfer state when casting
  108. * begins.
  109. * @const {!Array.<!Array.<string>>}
  110. */
  111. shaka.cast.CastUtils.PlayerInitState = [
  112. ['getConfiguration', 'configure']
  113. ];
  114. /**
  115. * Player getter and setter methods that are used to transfer state after
  116. * after load() is resolved.
  117. * @const {!Array.<!Array.<string>>}
  118. */
  119. shaka.cast.CastUtils.PlayerInitAfterLoadState = [
  120. ['isTextTrackVisible', 'setTextTrackVisibility']
  121. ];
  122. /**
  123. * Player methods with no return value that are proxied while casting.
  124. * @const {!Array.<string>}
  125. */
  126. shaka.cast.CastUtils.PlayerVoidMethods = [
  127. 'addTextTrack',
  128. 'cancelTrickPlay',
  129. 'configure',
  130. 'resetConfiguration',
  131. 'selectTrack',
  132. 'setTextTrackVisibility',
  133. 'trickPlay'
  134. ];
  135. /**
  136. * Player methods returning a Promise that are proxied while casting.
  137. * @const {!Array.<string>}
  138. */
  139. shaka.cast.CastUtils.PlayerPromiseMethods = [
  140. // The opt_manifestFactory method is not supported.
  141. 'load',
  142. 'unload'
  143. ];
  144. /**
  145. * @typedef {{
  146. * video: Object,
  147. * player: Object,
  148. * manifest: ?string,
  149. * startTime: ?number
  150. * }}
  151. * @property {Object} video
  152. * Dictionary of video properties to be set.
  153. * @property {Object} player
  154. * Dictionary of player setters to be called.
  155. * @property {?string} manifest
  156. * The currently-selected manifest, if present.
  157. * @property {?number} startTime
  158. * The playback start time, if currently playing.
  159. */
  160. shaka.cast.CastUtils.InitStateType;
  161. /**
  162. * The namespace for Shaka messages on the cast bus.
  163. * @const {string}
  164. */
  165. shaka.cast.CastUtils.MESSAGE_NAMESPACE = 'urn:x-cast:com.google.shaka.v2';
  166. /**
  167. * Serialize as JSON, but specially encode things JSON will not otherwise
  168. * represent.
  169. * @param {?} thing
  170. * @return {string}
  171. */
  172. shaka.cast.CastUtils.serialize = function(thing) {
  173. return JSON.stringify(thing, function(key, value) {
  174. if (key == 'manager') {
  175. // ABR manager can't be serialized.
  176. return undefined;
  177. }
  178. if (typeof value == 'function') {
  179. // Functions can't be (safely) serialized.
  180. return undefined;
  181. }
  182. if (value instanceof Event || value instanceof shaka.util.FakeEvent) {
  183. // Events don't serialize to JSON well because of the DOM objects
  184. // and other complex objects they contain. So we strip these out.
  185. // Note that using Object.keys or JSON.stringify directly on the event
  186. // will not capture its properties. We must use a for loop.
  187. var simpleEvent = {};
  188. for (var eventKey in value) {
  189. var eventValue = value[eventKey];
  190. if (eventValue && typeof eventValue == 'object') {
  191. // Strip out non-null object types because they are complex and we
  192. // don't need them.
  193. } else if (eventKey in Event) {
  194. // Strip out keys that are found on Event itself because they are
  195. // class-level constants we don't need, like Event.MOUSEMOVE == 16.
  196. } else {
  197. simpleEvent[eventKey] = eventValue;
  198. }
  199. }
  200. return simpleEvent;
  201. }
  202. if (value instanceof TimeRanges) {
  203. // TimeRanges must be unpacked into plain data for serialization.
  204. return shaka.cast.CastUtils.unpackTimeRanges_(value);
  205. }
  206. if (typeof value == 'number') {
  207. // NaN and infinity cannot be represented directly in JSON.
  208. if (isNaN(value)) return 'NaN';
  209. if (isFinite(value)) return value;
  210. if (value < 0) return '-Infinity';
  211. return 'Infinity';
  212. }
  213. return value;
  214. });
  215. };
  216. /**
  217. * Deserialize JSON using our special encodings.
  218. * @param {string} str
  219. * @return {?}
  220. */
  221. shaka.cast.CastUtils.deserialize = function(str) {
  222. return JSON.parse(str, function(key, value) {
  223. if (value == 'NaN') {
  224. return NaN;
  225. } else if (value == '-Infinity') {
  226. return -Infinity;
  227. } else if (value == 'Infinity') {
  228. return Infinity;
  229. } else if (value && typeof value == 'object' &&
  230. value['__type__'] == 'TimeRanges') {
  231. // TimeRanges objects have been unpacked and sent as plain data.
  232. // Simulate the original TimeRanges object.
  233. return shaka.cast.CastUtils.simulateTimeRanges_(value);
  234. }
  235. return value;
  236. });
  237. };
  238. /**
  239. * @param {!TimeRanges} ranges
  240. * @return {Object}
  241. * @private
  242. */
  243. shaka.cast.CastUtils.unpackTimeRanges_ = function(ranges) {
  244. var obj = {
  245. '__type__': 'TimeRanges', // a signal to deserialize
  246. 'length': ranges.length,
  247. 'start': [],
  248. 'end': []
  249. };
  250. for (var i = 0; i < ranges.length; ++i) {
  251. obj['start'].push(ranges.start(i));
  252. obj['end'].push(ranges.end(i));
  253. }
  254. return obj;
  255. };
  256. /**
  257. * Creates a simulated TimeRanges object from data sent by the cast receiver.
  258. * @param {?} obj
  259. * @return {{
  260. * length: number,
  261. * start: function(number): number,
  262. * end: function(number): number
  263. * }}
  264. * @private
  265. */
  266. shaka.cast.CastUtils.simulateTimeRanges_ = function(obj) {
  267. return {
  268. length: obj.length,
  269. // NOTE: a more complete simulation would throw when |i| was out of range,
  270. // but for simplicity we will assume a well-behaved application that uses
  271. // length instead of catch to stop iterating.
  272. start: function(i) { return obj.start[i]; },
  273. end: function(i) { return obj.end[i]; }
  274. };
  275. };