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. 'streaming',
  85. 'texttrackvisibility',
  86. 'timelineregionadded',
  87. 'timelineregionenter',
  88. 'timelineregionexit',
  89. 'trackschanged',
  90. 'unloading',
  91. ];
  92. /**
  93. * Player getter methods that are proxied while casting.
  94. * The key is the method, the value is the frequency of updates.
  95. * Frequency 1 translates to every update; frequency 2 to every 2 updates, etc.
  96. * @const {!Object.<string, number>}
  97. */
  98. shaka.cast.CastUtils.PlayerGetterMethods = {
  99. // NOTE: The 'drmInfo' property is not proxied, as it is very large.
  100. 'getAssetUri': 2,
  101. 'getAudioLanguages': 2,
  102. 'getAudioLanguagesAndRoles': 2,
  103. 'getBufferedInfo': 2,
  104. // NOTE: The 'getSharedConfiguration' property is not proxied as it would
  105. // not be possible to share a reference.
  106. 'getConfiguration': 2,
  107. 'getExpiration': 2,
  108. // NOTE: The 'getManifest' property is not proxied, as it is very large.
  109. // TODO(vaage): Remove |getManifestUri| references in v2.6.
  110. // NOTE: The 'getManifestUri' property is not proxied, as CastProxy has a
  111. // handler for it.
  112. 'getPlaybackRate': 2,
  113. 'getTextLanguages': 2,
  114. 'getTextLanguagesAndRoles': 2,
  115. 'getTextTracks': 2,
  116. 'getStats': 5,
  117. 'usingEmbeddedTextTrack': 2,
  118. 'getVariantTracks': 2,
  119. 'isAudioOnly': 10,
  120. 'isBuffering': 1,
  121. 'isInProgress': 1,
  122. 'isLive': 10,
  123. 'isTextTrackVisible': 1,
  124. 'keySystem': 10,
  125. 'seekRange': 1,
  126. };
  127. /**
  128. * Player getter methods that are proxied while casting, but only when casting
  129. * a livestream.
  130. * The key is the method, the value is the frequency of updates.
  131. * Frequency 1 translates to every update; frequency 2 to every 2 updates, etc.
  132. * @const {!Object.<string, number>}
  133. */
  134. shaka.cast.CastUtils.PlayerGetterMethodsThatRequireLive = {
  135. 'getPlayheadTimeAsDate': 1,
  136. 'getPresentationStartTimeAsDate': 20,
  137. };
  138. /**
  139. * Player getter and setter methods that are used to transfer state when casting
  140. * begins.
  141. * @const {!Array.<!Array.<string>>}
  142. */
  143. shaka.cast.CastUtils.PlayerInitState = [
  144. ['getConfiguration', 'configure'],
  145. ];
  146. /**
  147. * Player getter and setter methods that are used to transfer state after
  148. * load() is resolved.
  149. * @const {!Array.<!Array.<string>>}
  150. */
  151. shaka.cast.CastUtils.PlayerInitAfterLoadState = [
  152. ['isTextTrackVisible', 'setTextTrackVisibility'],
  153. ];
  154. /**
  155. * Player methods with no return value that are proxied while casting.
  156. * @const {!Array.<string>}
  157. */
  158. shaka.cast.CastUtils.PlayerVoidMethods = [
  159. 'addTextTrack',
  160. 'cancelTrickPlay',
  161. 'configure',
  162. 'resetConfiguration',
  163. 'retryStreaming',
  164. 'selectAudioLanguage',
  165. 'selectEmbeddedTextTrack',
  166. 'selectTextLanguage',
  167. 'selectTextTrack',
  168. 'selectVariantTrack',
  169. 'setTextTrackVisibility',
  170. 'trickPlay',
  171. ];
  172. /**
  173. * Player methods returning a Promise that are proxied while casting.
  174. * @const {!Array.<string>}
  175. */
  176. shaka.cast.CastUtils.PlayerPromiseMethods = [
  177. 'attach',
  178. 'detach',
  179. // The manifestFactory parameter of load is not supported.
  180. 'load',
  181. 'unload',
  182. ];
  183. /**
  184. * @typedef {{
  185. * video: Object,
  186. * player: Object,
  187. * manifest: ?string,
  188. * startTime: ?number
  189. * }}
  190. * @property {Object} video
  191. * Dictionary of video properties to be set.
  192. * @property {Object} player
  193. * Dictionary of player setters to be called.
  194. * @property {?string} manifest
  195. * The currently-selected manifest, if present.
  196. * @property {?number} startTime
  197. * The playback start time, if currently playing.
  198. */
  199. shaka.cast.CastUtils.InitStateType;
  200. /**
  201. * The namespace for Shaka messages on the cast bus.
  202. * @const {string}
  203. */
  204. shaka.cast.CastUtils.SHAKA_MESSAGE_NAMESPACE = 'urn:x-cast:com.google.shaka.v2';
  205. /**
  206. * The namespace for generic messages on the cast bus.
  207. * @const {string}
  208. */
  209. shaka.cast.CastUtils.GENERIC_MESSAGE_NAMESPACE =
  210. 'urn:x-cast:com.google.cast.media';
  211. /**
  212. * Serialize as JSON, but specially encode things JSON will not otherwise
  213. * represent.
  214. * @param {?} thing
  215. * @return {string}
  216. */
  217. shaka.cast.CastUtils.serialize = function(thing) {
  218. return JSON.stringify(thing, function(key, value) {
  219. if (typeof value == 'function') {
  220. // Functions can't be (safely) serialized.
  221. return undefined;
  222. }
  223. if (value instanceof Event || value instanceof shaka.util.FakeEvent) {
  224. // Events don't serialize to JSON well because of the DOM objects
  225. // and other complex objects they contain, so we strip these out.
  226. // Note that using Object.keys or JSON.stringify directly on the event
  227. // will not capture its properties. We must use a for loop.
  228. let simpleEvent = {};
  229. for (let eventKey in value) {
  230. let eventValue = value[eventKey];
  231. if (eventValue && typeof eventValue == 'object') {
  232. if (eventKey == 'detail') {
  233. // Keep the detail value, because it contains important information
  234. // for diagnosing errors.
  235. simpleEvent[eventKey] = eventValue;
  236. }
  237. // Strip out non-null object types because they are complex and we
  238. // don't need them.
  239. } else if (eventKey in Event) {
  240. // Strip out keys that are found on Event itself because they are
  241. // class-level constants we don't need, like Event.MOUSEMOVE == 16.
  242. } else {
  243. simpleEvent[eventKey] = eventValue;
  244. }
  245. }
  246. return simpleEvent;
  247. }
  248. if (value instanceof TimeRanges) {
  249. // TimeRanges must be unpacked into plain data for serialization.
  250. return shaka.cast.CastUtils.unpackTimeRanges_(value);
  251. }
  252. if (typeof value == 'number') {
  253. // NaN and infinity cannot be represented directly in JSON.
  254. if (isNaN(value)) return 'NaN';
  255. if (isFinite(value)) return value;
  256. if (value < 0) return '-Infinity';
  257. return 'Infinity';
  258. }
  259. return value;
  260. });
  261. };
  262. /**
  263. * Deserialize JSON using our special encodings.
  264. * @param {string} str
  265. * @return {?}
  266. */
  267. shaka.cast.CastUtils.deserialize = function(str) {
  268. return JSON.parse(str, function(key, value) {
  269. if (value == 'NaN') {
  270. return NaN;
  271. } else if (value == '-Infinity') {
  272. return -Infinity;
  273. } else if (value == 'Infinity') {
  274. return Infinity;
  275. } else if (value && typeof value == 'object' &&
  276. value['__type__'] == 'TimeRanges') {
  277. // TimeRanges objects have been unpacked and sent as plain data.
  278. // Simulate the original TimeRanges object.
  279. return shaka.cast.CastUtils.simulateTimeRanges_(value);
  280. }
  281. return value;
  282. });
  283. };
  284. /**
  285. * @param {!TimeRanges} ranges
  286. * @return {Object}
  287. * @private
  288. */
  289. shaka.cast.CastUtils.unpackTimeRanges_ = function(ranges) {
  290. let obj = {
  291. '__type__': 'TimeRanges', // a signal to deserialize
  292. 'length': ranges.length,
  293. 'start': [],
  294. 'end': [],
  295. };
  296. for (let i = 0; i < ranges.length; ++i) {
  297. obj['start'].push(ranges.start(i));
  298. obj['end'].push(ranges.end(i));
  299. }
  300. return obj;
  301. };
  302. /**
  303. * Creates a simulated TimeRanges object from data sent by the cast receiver.
  304. * @param {?} obj
  305. * @return {{
  306. * length: number,
  307. * start: function(number): number,
  308. * end: function(number): number
  309. * }}
  310. * @private
  311. */
  312. shaka.cast.CastUtils.simulateTimeRanges_ = function(obj) {
  313. return {
  314. length: obj.length,
  315. // NOTE: a more complete simulation would throw when |i| was out of range,
  316. // but for simplicity we will assume a well-behaved application that uses
  317. // length instead of catch to stop iterating.
  318. start: function(i) { return obj.start[i]; },
  319. end: function(i) { return obj.end[i]; },
  320. };
  321. };