shaka-player 4.0.2 → 4.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. package/.github/workflows/build-and-test.yaml +67 -15
  2. package/.github/workflows/compute-incremental-coverage.py +251 -0
  3. package/.github/workflows/report-incremental-coverage.yaml +39 -0
  4. package/.github/workflows/selenium-lab-tests.yaml +44 -1
  5. package/AUTHORS +2 -0
  6. package/CHANGELOG.md +43 -0
  7. package/CONTRIBUTORS +4 -0
  8. package/README.md +20 -6
  9. package/demo/common/assets.js +1 -1
  10. package/demo/index.html +1 -3
  11. package/demo/main.js +3 -3
  12. package/dist/controls.css +1 -1
  13. package/dist/demo.compiled.debug.js +3 -3
  14. package/dist/demo.compiled.debug.map +2 -2
  15. package/dist/demo.compiled.js +3 -3
  16. package/dist/demo.compiled.map +2 -2
  17. package/dist/deps.js +3 -3
  18. package/dist/locales.js +29 -29
  19. package/dist/receiver.compiled.debug.js +1 -1
  20. package/dist/receiver.compiled.debug.map +1 -1
  21. package/dist/receiver.compiled.js +1 -1
  22. package/dist/receiver.compiled.map +1 -1
  23. package/dist/shaka-player.compiled.d.ts +12 -1
  24. package/dist/shaka-player.compiled.debug.d.ts +12 -1
  25. package/dist/shaka-player.compiled.debug.externs.js +52 -40
  26. package/dist/shaka-player.compiled.debug.js +269 -256
  27. package/dist/shaka-player.compiled.debug.map +4 -4
  28. package/dist/shaka-player.compiled.externs.js +52 -40
  29. package/dist/shaka-player.compiled.js +739 -730
  30. package/dist/shaka-player.compiled.map +4 -4
  31. package/dist/shaka-player.ui.d.ts +12 -1
  32. package/dist/shaka-player.ui.debug.d.ts +12 -1
  33. package/dist/shaka-player.ui.debug.externs.js +52 -40
  34. package/dist/shaka-player.ui.debug.js +296 -282
  35. package/dist/shaka-player.ui.debug.map +4 -4
  36. package/dist/shaka-player.ui.externs.js +52 -40
  37. package/dist/shaka-player.ui.js +885 -878
  38. package/dist/shaka-player.ui.map +4 -4
  39. package/docs/tutorials/ui.md +5 -5
  40. package/docs/tutorials/upgrade.md +1 -0
  41. package/docs/tutorials/welcome.md +6 -3
  42. package/externs/shaka/player.js +4 -2
  43. package/karma.conf.js +3 -11
  44. package/lib/ads/client_side_ad_manager.js +1 -1
  45. package/lib/dash/dash_parser.js +5 -0
  46. package/lib/hls/hls_parser.js +183 -70
  47. package/lib/media/drm_engine.js +1 -1
  48. package/lib/media/media_source_engine.js +57 -24
  49. package/lib/media/presentation_timeline.js +24 -1
  50. package/lib/media/segment_index.js +26 -1
  51. package/lib/media/segment_reference.js +10 -3
  52. package/lib/media/streaming_engine.js +4 -4
  53. package/lib/media/time_ranges_utils.js +46 -0
  54. package/lib/player.js +1 -6
  55. package/lib/polyfill/media_capabilities.js +9 -0
  56. package/lib/polyfill/videoplaybackquality.js +3 -1
  57. package/lib/text/cue.js +0 -5
  58. package/lib/text/simple_text_displayer.js +21 -9
  59. package/lib/text/ui_text_displayer.js +83 -19
  60. package/lib/util/buffer_utils.js +3 -0
  61. package/lib/util/object_utils.js +1 -0
  62. package/lib/util/platform.js +19 -1
  63. package/lib/util/stream_utils.js +24 -3
  64. package/lib/util/string_utils.js +102 -13
  65. package/maintained-branches.md +15 -0
  66. package/package.json +3 -5
  67. package/test/hls/hls_parser_unit.js +102 -8
  68. package/test/media/drm_engine_integration.js +24 -8
  69. package/test/media/media_source_engine_integration.js +24 -10
  70. package/test/media/media_source_engine_unit.js +53 -38
  71. package/test/media/streaming_engine_unit.js +28 -27
  72. package/test/test/util/fake_media_source_engine.js +10 -9
  73. package/test/text/simple_text_displayer_unit.js +27 -0
  74. package/test/text/ui_text_displayer_unit.js +65 -0
  75. package/test/util/string_utils_unit.js +46 -8
  76. package/ui/text_selection.js +2 -2
  77. package/app-engine/README.md +0 -9
  78. package/app-engine/demo-version-index/README.md +0 -14
  79. package/app-engine/demo-version-index/app.yaml +0 -28
  80. package/app-engine/demo-version-index/generate.py +0 -164
  81. package/app-engine/demo-version-index/main.py +0 -11
  82. package/app-engine/demo-version-index/requirements.txt +0 -5
  83. package/app-engine/demo-version-index/templates/index.html +0 -114
  84. package/app-engine/shaka-player-demo/README.md +0 -18
  85. package/app-engine/shaka-player-demo/app.yaml +0 -61
  86. package/app-engine/shaka-player-demo/main.py +0 -95
  87. package/app-engine/shaka-player-demo/posters/10yo.png +0 -0
  88. package/app-engine/shaka-player-demo/posters/10yo.xcf +0 -0
  89. package/app-engine/shaka-player-demo/posters/1yo.jpg +0 -0
  90. package/app-engine/shaka-player-demo/posters/2yo.png +0 -0
  91. package/app-engine/shaka-player-demo/posters/2yo.xcf +0 -0
  92. package/app-engine/shaka-player-demo/posters/3yo.png +0 -0
  93. package/app-engine/shaka-player-demo/posters/3yo.xcf +0 -0
  94. package/app-engine/shaka-player-demo/posters/4yo.png +0 -0
  95. package/app-engine/shaka-player-demo/posters/4yo.xcf +0 -0
  96. package/app-engine/shaka-player-demo/posters/5yo.png +0 -0
  97. package/app-engine/shaka-player-demo/posters/5yo.xcf +0 -0
  98. package/app-engine/shaka-player-demo/posters/6yo.png +0 -0
  99. package/app-engine/shaka-player-demo/posters/6yo.xcf +0 -0
  100. package/app-engine/shaka-player-demo/posters/7yo.png +0 -0
  101. package/app-engine/shaka-player-demo/posters/7yo.xcf +0 -0
  102. package/app-engine/shaka-player-demo/posters/8yo.png +0 -0
  103. package/app-engine/shaka-player-demo/posters/8yo.xcf +0 -0
  104. package/app-engine/shaka-player-demo/posters/9yo.png +0 -0
  105. package/app-engine/shaka-player-demo/posters/9yo.xcf +0 -0
  106. package/app-engine/shaka-player-demo/posters/audioOnly.gif +0 -0
  107. package/app-engine/shaka-player-demo/posters/audioRickRoll.gif +0 -0
  108. package/app-engine/shaka-player-demo/posters/colorbars.gif +0 -0
  109. package/app-engine/shaka-player-demo/posters/fireworks.jpg +0 -0
  110. package/app-engine/shaka-player-demo/posters/party-hat.png +0 -0
  111. package/app-engine/shaka-player-demo/posters/sources.txt +0 -46
  112. package/app-engine/shaka-player-demo/posters/standard.png +0 -0
  113. package/app-engine/shaka-player-demo/posters/trek.jpg +0 -0
  114. package/app-engine/shaka-player-demo/posters/valentines.png +0 -0
  115. package/app-engine/shaka-player-demo/posters/zombie.jpg +0 -0
  116. package/app-engine/shaka-player-demo/posters.py +0 -110
  117. package/app-engine/shaka-player-demo/requirements.txt +0 -5
  118. package/app-engine/shaka-player-demo/time.txt +0 -1
  119. package/build/README.md +0 -127
  120. package/build/__init__.py +0 -0
  121. package/build/all.py +0 -150
  122. package/build/apps.py +0 -186
  123. package/build/build.py +0 -392
  124. package/build/check.py +0 -354
  125. package/build/checkversion.py +0 -84
  126. package/build/compiler.py +0 -513
  127. package/build/conformance.textproto +0 -335
  128. package/build/docker/Dockerfile +0 -20
  129. package/build/docker/docker-compose.yml +0 -10
  130. package/build/docs.py +0 -54
  131. package/build/eslint-plugin-shaka-rules/arg-comment-spacing.js +0 -72
  132. package/build/eslint-plugin-shaka-rules/index.js +0 -24
  133. package/build/eslint-plugin-shaka-rules/package.json +0 -7
  134. package/build/eslint-plugin-shaka-rules/private.js +0 -109
  135. package/build/gendeps.py +0 -71
  136. package/build/generateExterns.js +0 -867
  137. package/build/generateLocalizations.py +0 -262
  138. package/build/generateTsDefs.py +0 -137
  139. package/build/install-linux-prereqs.sh +0 -136
  140. package/build/license-header +0 -5
  141. package/build/misspellings.txt +0 -39
  142. package/build/pixelsChanged.js +0 -35
  143. package/build/shaka-lab.yaml +0 -161
  144. package/build/shakaBuildHelpers.py +0 -356
  145. package/build/stats.py +0 -816
  146. package/build/subprocessWindowsPatch.py +0 -103
  147. package/build/test.py +0 -541
  148. package/build/types/ads +0 -8
  149. package/build/types/cast +0 -6
  150. package/build/types/cea +0 -13
  151. package/build/types/complete +0 -9
  152. package/build/types/core +0 -108
  153. package/build/types/dash +0 -8
  154. package/build/types/hls +0 -9
  155. package/build/types/manifests +0 -5
  156. package/build/types/networking +0 -6
  157. package/build/types/offline +0 -22
  158. package/build/types/polyfill +0 -18
  159. package/build/types/text +0 -11
  160. package/build/types/ui +0 -39
  161. package/build/updateScreenshots.py +0 -85
  162. package/build/wrapper.template.js +0 -52
package/build/stats.py DELETED
@@ -1,816 +0,0 @@
1
- #!/usr/bin/env python
2
- #
3
- # Copyright 2016 Google LLC
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
- """A program for analyzing the Shaka compiled sources.
18
-
19
- This can be used to find areas that can be removed if not needed. This uses
20
- the source map (i.e. shaka-player.compiled.debug.map) to find the compiled code
21
- size, see:
22
- https://github.com/mattrobenolt/python-sourcemap
23
- http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/
24
-
25
- This script can output four different stats in two different formats:
26
- - The size of functions and namespaces.
27
- - The dependencies between types (in plain or DOT format).
28
- - The dependencies between functions (in plain or DOT format).
29
- - All tokens in the source map.
30
-
31
- The dependencies can be outputted in DOT format which can be used with graph
32
- programs to display a visual layout of the dependencies. For example, using
33
- graphviz:
34
-
35
- ./stats.py -c -d | fdb -Goverlap=prism | neato -n2 -Tsvg > out.svg
36
- """
37
-
38
- from __future__ import print_function
39
-
40
- import argparse
41
- import json
42
- import logging
43
- import math
44
- import os
45
- import string
46
-
47
- import shakaBuildHelpers
48
-
49
-
50
- # A Base64 VLQ digit can represent 5 bits, so it is Base32.
51
- VLQ_BASE_SHIFT = 5
52
- VLQ_BASE = 1 << VLQ_BASE_SHIFT
53
-
54
- # A mask of bits for a VLQ digit (11111), 31 decimal
55
- VLQ_BASE_MASK = VLQ_BASE - 1
56
-
57
- # The continuation bit is the 6th bit
58
- VLQ_CONTINUATION_BIT = VLQ_BASE
59
-
60
- # Don't use Base64 lib since it is not a real Base64 string; it simply
61
- # decodes each character to a single Base64 number.
62
- B64 = dict((c, i) for i, c in
63
- enumerate('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
64
- '0123456789+/'))
65
-
66
-
67
- def from_vlq_signed(value):
68
- """Converts a VLQ number to a normal signed number.
69
-
70
- Args:
71
- value: A number decoded from a VLQ string.
72
-
73
- Returns:
74
- an integer.
75
- """
76
- negative = (value & 1) == 1
77
- value >>= 1
78
- return -value if negative else value
79
-
80
-
81
- class Segment(object):
82
- """Defines an entry in the source map.
83
-
84
- Members:
85
- dst_col_offset - The offset of the destination column from the previous
86
- segment.
87
- name_offset - If not None, the offset of the name index from the previous
88
- segment.
89
- """
90
-
91
- def __init__(self, data):
92
- self.dst_col_offset = data[0]
93
- self.name_offset = data[4] if len(data) > 4 else None
94
-
95
-
96
- def decode_segment(segment):
97
- """Decodes VLQ values from the given segment.
98
-
99
- Args:
100
- segment: A string containing the encoded segment text.
101
-
102
- Returns:
103
- the parsed Segment.
104
- """
105
-
106
- values = []
107
- cur, shift = 0, 0
108
-
109
- for c in segment:
110
- digit = B64[c]
111
- cont = (digit & VLQ_CONTINUATION_BIT) != 0
112
- digit &= VLQ_BASE_MASK
113
- cur += digit << shift
114
- shift += VLQ_BASE_SHIFT
115
-
116
- if not cont:
117
- values.append(from_vlq_signed(cur))
118
- cur, shift = 0, 0
119
-
120
- # A valid VLQ string should not have dangling bits.
121
- assert cur == 0
122
- assert shift == 0
123
- return Segment(values)
124
-
125
-
126
- class Token(object):
127
- """A Token represents one JavaScript symbol.
128
-
129
- For example, this can be a variable or an equals sign. If this is a variable
130
- or the keyword 'function' it will usually have a name which indicates what it
131
- originally was defined. But there are also tokens such as ; and ( which appear
132
- as tokens in the map but to not have explicit name (see isFunction).
133
-
134
- Members:
135
- dst_line - Line index in compiled code
136
- dst_col - Column index in compiled code
137
- name - Name of the token; or None
138
- """
139
-
140
- def __init__(self, dst_line, dst_col, name=None):
141
- self.dst_line = dst_line
142
- self.dst_col = dst_col
143
- self.name = name
144
-
145
- def __str__(self):
146
- return str(self.name)
147
-
148
-
149
- def decode_mappings(line_data, names):
150
- """Decodes a mappings line of text.
151
-
152
- Args:
153
- line_data: A string containing the mapping line.
154
- names: An array of strings containing the names of the objects.
155
-
156
- Returns:
157
- a list of Tokens
158
- """
159
-
160
- tokens = []
161
-
162
- lines = line_data.split(';')
163
- name_id = 0
164
- for dst_line, line in enumerate(lines):
165
- dst_col = 0
166
- segments = line.split(',')
167
- for segment in segments:
168
- if not segment:
169
- continue
170
-
171
- segment = decode_segment(segment)
172
- dst_col += segment.dst_col_offset
173
-
174
- # segment.dst_col can be negative (more useful in names below); however
175
- # after applying a negative offset, the result must still be positive.
176
- assert dst_col >= 0
177
-
178
- name = None
179
- if segment.name_offset is not None:
180
- name_id += segment.name_offset
181
- assert name_id >= 0
182
- name = names[name_id]
183
-
184
- tokens.append(Token(dst_line, dst_col, name))
185
-
186
- return tokens
187
-
188
-
189
- def is_function(token, lines):
190
- """Determines if the given token is the start of a function.
191
-
192
- All function definitions are assumed to have a name field and the token in
193
- the compiled source is the keyword 'function'. Sometimes the function is
194
- defined on the previous semicolon and sometimes that semicolon appears on
195
- the previous line.
196
-
197
- Args:
198
- token: The Token to check.
199
- lines: An array of compiled code lines.
200
-
201
- Returns:
202
- whether the token is a function.
203
- """
204
-
205
- # All functions have a name.
206
- if not token.name:
207
- return False
208
-
209
- # Sometimes a function token starts with the previous ;
210
- # Also sometimes the token starts on the ; that is on the previous
211
- # line.
212
- partial_line = lines[token.dst_line][token.dst_col:]
213
- if partial_line == ';\n':
214
- if len(lines) == token.dst_line + 1:
215
- return False
216
- else:
217
- return lines[token.dst_line + 1].startswith('function')
218
- else:
219
- return (partial_line.startswith('function') or
220
- partial_line.startswith(';function'))
221
-
222
-
223
- def read_function(token_iter, prev, prev_index, lines, callback):
224
- """Reads a function from the token stream.
225
-
226
- The function token should already be consumed.
227
-
228
- Args:
229
- token_iter: An iterator of the tokens.
230
- prev: The token containing the function definition.
231
- prev_index: The index of the previous token.
232
- lines: An array of compiled code lines.
233
- callback: A callback type used to create the data. See traverse_tokens.
234
-
235
- Returns:
236
- an array of State objects in a format controlled by the callback (see
237
- traverse_tokens).
238
- """
239
-
240
- brackets = 0
241
- read = False
242
- ret = []
243
-
244
- partial_line = lines[prev.dst_line][prev.dst_col:]
245
- state = callback(prev, prev_index)
246
-
247
- try:
248
- while not read or brackets > 0:
249
- index, token = next(token_iter)
250
- partial_line = lines[token.dst_line][token.dst_col:]
251
-
252
- # Recursively read functions. Sometimes functions are defined nested.
253
- # This doesn't happen that often, and never for Shaka methods, so it does
254
- # not count it twice since the size of this method includes the nested
255
- # function.
256
- if is_function(token, lines):
257
- ret += read_function(token_iter, token, index, lines, callback)
258
- else:
259
- state.add(token, index)
260
-
261
- if partial_line.startswith('{}'):
262
- read = True
263
- elif partial_line[0] == '{':
264
- brackets += 1
265
- read = True
266
- elif partial_line[0] == '}':
267
- brackets -= 1
268
- # When we run out of tokens, simply ignore it. A parent call will not see
269
- # this error; but it will continue and the next call to 'next' will fail
270
- # with another StopIteration. This ensures that the last State object
271
- # is included for invalid content.
272
- except StopIteration:
273
- pass
274
-
275
- temp = state.build()
276
- if temp:
277
- ret.append(temp)
278
-
279
- return ret
280
-
281
-
282
- def traverse_tokens(tokens, lines, callback):
283
- """Traverses a list of tokens to identify functions.
284
-
285
- Then uses a callback to perform some work on the functions. Each function
286
- seen gets a new State object created from the given callback method; there is
287
- a single State for global code which is given None in the constructor. Then,
288
- each token seen is passed to the 'add' method of the State. This is used by
289
- the State to either calculate sizes, print tokens, or detect dependencies.
290
- The 'build' method is called at the end of the function to create a result
291
- object that is returned as an array at the end.
292
-
293
- Args:
294
- tokens: An array of Tokens.
295
- lines: An array of compiled code lines.
296
- callback: A constructor that returns a state object. It takes a start
297
- token or None if outside a function. It has two member functions
298
- add - accepts the current token and the token's index.
299
- build - returns an object to be added to the results.
300
-
301
- Returns:
302
- an array of State objects in a format controlled by the callback.
303
- """
304
-
305
- ret = []
306
- state = callback(None, None)
307
- # Create a token iterator. This is used to read tokens from the array. We
308
- # cannot use a for loop because the iterator is passed to readFunction.
309
- token_iter = enumerate(tokens)
310
- try:
311
- while True:
312
- index, token = next(token_iter)
313
-
314
- if is_function(token, lines):
315
- ret += read_function(token_iter, token, index, lines, callback)
316
- else:
317
- state.add(token, index)
318
- except StopIteration:
319
- pass
320
-
321
- temp = state.build()
322
- if temp:
323
- ret.append(temp)
324
- return ret
325
-
326
-
327
- class FunctionSize(object):
328
- """Contains information about a function's size."""
329
-
330
- def __init__(self, name, size):
331
- self.name = name
332
- self.size = size
333
-
334
-
335
- def print_tokens(tokens, lines, funcs):
336
- """Prints the given tokens.
337
-
338
- Args:
339
- tokens: An array of Tokens.
340
- lines: An array of compiled code lines.
341
- funcs: An array of FunctionSize.
342
- """
343
-
344
- class State(object):
345
- """Defines the current parser state."""
346
-
347
- def __init__(self, token, index):
348
- # The start of a function, or the global start.
349
- self.name = token.name if token else None
350
- if token:
351
- self._print_token('>', token, index)
352
-
353
- def _print_token(self, prefix, token, index):
354
- partial_line = lines[token.dst_line][token.dst_col:]
355
- if len(tokens) > index + 1:
356
- next_ = tokens[index + 1]
357
- if next_.dst_line == token.dst_line:
358
- partial_line = lines[token.dst_line][token.dst_col:next_.dst_col]
359
- token_text = partial_line[:10].replace('\n', '').rjust(12)
360
- print('%s %4d %4d %12s %s' % (prefix, token.dst_line, token.dst_col,
361
- token_text, token.name))
362
-
363
- def add(self, token, index):
364
- """Parses the given token.
365
-
366
- Args:
367
- token: The token to add.
368
- index: The index of the token in the original array.
369
- """
370
- prefix = None
371
- if not self.name:
372
- prefix = '!'
373
- elif lines[token.dst_line][token.dst_col:token.dst_col+2] == '{}':
374
- prefix = ' '
375
- elif lines[token.dst_line][token.dst_col] == '{':
376
- prefix = '+'
377
- elif lines[token.dst_line][token.dst_col] == '}':
378
- prefix = '-'
379
- else:
380
- prefix = ' '
381
-
382
- self._print_token(prefix, token, index)
383
-
384
- def build(self):
385
- if not self.name:
386
- return
387
-
388
- # The end of a function. Print the size of this function.
389
- size = 0
390
- this_func = [t for t in funcs if t.name == self.name]
391
- if this_func:
392
- size = this_func[0].size
393
- print('X %s %d' % (self.name, size))
394
-
395
- traverse_tokens(tokens, lines, State)
396
-
397
-
398
- class FunctionDependencies(object):
399
- """Contains information about a function's dependencies."""
400
-
401
- def __init__(self, name, deps):
402
- self.name = name
403
- self.deps = deps
404
-
405
-
406
- def process_deps(tokens, lines, is_class):
407
- """Processes the tokens into function or class dependencies.
408
-
409
- Args:
410
- tokens: An array of Tokens.
411
- lines: An array of compiled code lines.
412
- is_class: Whether to create a class graph instead of a function graph.
413
-
414
- Returns:
415
- an array of FunctionDependencies.
416
- """
417
-
418
- class State(object):
419
- """Defines the current parser state."""
420
-
421
- def __init__(self, token, _):
422
- self.deps = []
423
- self.name, self.parts = self._create_parts(token)
424
-
425
- def _create_parts(self, token):
426
- """Creates an array of name parts.
427
-
428
- Args:
429
- token: The token to create the name from.
430
-
431
- Returns:
432
- A tuple of the name and the array of name parts.
433
- """
434
- if not token or not token.name:
435
- return (None, None)
436
- parts = token.name.split('.')
437
- name = token.name
438
-
439
- # Instance methods are the same as static methods.
440
- if len(parts) > 2 and parts[-2] == 'prototype':
441
- del parts[-2]
442
-
443
- # Strip function names if class graph; also remove it from the name.
444
- if is_class:
445
- if parts[-1][0] in string.ascii_lowercase:
446
- del parts[-1]
447
- name = '.'.join(parts)
448
-
449
- return (name, parts)
450
-
451
- def add(self, token, _):
452
- """Parses the given token.
453
-
454
- Args:
455
- token: The token to parse.
456
- """
457
- # Ignore symbols outside a function. Only care about function
458
- # references and only those that reference our code.
459
- if not self.name or not token.name or not token.name.startswith('shaka.'):
460
- return
461
-
462
- name, other_parts = self._create_parts(token)
463
-
464
- # Get the index of the first different namespace.
465
- count = min(len(self.parts), len(other_parts))
466
- i = 0
467
- while i < count and self.parts[i] == other_parts[i]:
468
- i += 1
469
-
470
- # Ignore use of members of the same object:
471
- # OfflineVideoSource.configure and OfflineVideoSource.store
472
- if (i == count - 1 or i == count) and len(self.parts) == len(other_parts):
473
- return
474
-
475
- # Ignore use of the constructor of the same type:
476
- # OfflineVideoSource and OfflineVideoSource.store
477
- if i == count and abs(len(self.parts) - len(other_parts)) == 1:
478
- return
479
-
480
- # Add the dependency.
481
- if name not in self.deps:
482
- self.deps.append(name)
483
-
484
- def build(self):
485
- return FunctionDependencies(self.name, self.deps) if self.name else None
486
-
487
- ret = traverse_tokens(tokens, lines, State)
488
- assert ret
489
- ret = sorted(ret, key=lambda key: key.name)
490
-
491
- # We need to collapse duplicates.
492
- i = 0
493
- while i + 1 < len(ret):
494
- if ret[i].name == ret[i + 1].name:
495
- for dep in ret[i + 1].deps:
496
- if dep not in ret[i].deps:
497
- ret[i].deps.append(dep)
498
- del ret[i + 1]
499
- else:
500
- i += 1
501
-
502
- return ret
503
-
504
-
505
- def process_sizes(tokens, lines):
506
- """Processes an array of tokens into function lengths.
507
-
508
- Args:
509
- tokens: An array of Tokens.
510
- lines: An array of compiled code lines.
511
-
512
- Returns:
513
- an array of FunctionSizes sorted on name.
514
- """
515
-
516
- class State(object):
517
- """Defines the current parser state."""
518
-
519
- def __init__(self, token, _):
520
- self.name = token.name if token else None
521
- self.size = 0
522
- self.start = token.dst_col if token else None
523
- self.line = token.dst_line if token else None
524
-
525
- def add(self, token, _):
526
- """Parses the given token.
527
-
528
- Args:
529
- token: The token to parse.
530
- """
531
- # Ignore outside a function
532
- if not self.name:
533
- return
534
- # If we skipped to the next line, include the code to the end of the line.
535
- # If we skipped multiple lines, include the whole line. This will most
536
- # likely never happen since the compiled code usually has new lines on
537
- # function boundaries.
538
- assert token.dst_line >= self.line
539
- while token.dst_line != self.line:
540
- self.size += len(lines[self.line]) - self.start
541
- self.line += 1
542
- self.start = 0
543
-
544
- # Keep increasing the size. We can't simply keep the start and measure
545
- # at the end since we are not given the end token in build().
546
- self.size += token.dst_col - self.start
547
- self.start = token.dst_col
548
-
549
- def build(self):
550
- return FunctionSize(self.name, self.size) if self.name else None
551
-
552
- ret = traverse_tokens(tokens, lines, State)
553
- assert ret
554
-
555
- ret = [k for k in ret if k.name and
556
- (k.name.startswith('shaka.') or k.name.startswith('goog.'))]
557
- ret = sorted(ret, key=lambda key: key.name)
558
-
559
- # We need to collapse duplicates.
560
- i = 0
561
- while i + 1 < len(ret):
562
- if ret[i].name == ret[i + 1].name:
563
- ret[i].size += ret[i + 1].size
564
- del ret[i + 1]
565
- else:
566
- i += 1
567
-
568
- return ret
569
-
570
-
571
- def print_tree(results, indent, callback, end_callback):
572
- """Prints the results in an indented format.
573
-
574
- Args:
575
- results: An array of FunctionSizes sorted on name.
576
- indent: A number to indent.
577
- callback: A callback function to print the data. Accepts a title, an
578
- indentation, and a sublist of the items in that group.
579
- end_callback: A callback function called after a group; can be None.
580
- """
581
-
582
- # This is used both when printing sizes and when printing dependencies in
583
- # DOT format. This recursively creates groups of items with the same prefix.
584
- # e.g.
585
- # shaka
586
- # shaka.util
587
- # shaka.util.FailoverUri
588
- # shaka.util.TypedBind
589
- # shaka.player
590
- # ...
591
-
592
- if len(results) <= 1:
593
- callback(None, indent, results)
594
- return
595
-
596
- # We want to group-by prefixes for the elements. Since it is sorted, we
597
- # can find the overall prefix length.
598
- first = results[0].name.split('.')
599
- last = results[-1].name.split('.')
600
- prefix = 0
601
- while (prefix < len(first) and prefix < len(last)
602
- and first[prefix] == last[prefix]):
603
- prefix += 1
604
-
605
- group = 0
606
- group_items = first
607
- if prefix == len(first):
608
- # This happens when the group has a first element of a class name and the
609
- # remaining are member functions. Remove the first element from this
610
- # group.
611
- group_items = results[1].name.split('.')
612
- group = 1
613
-
614
- # Start with second element, and go one more so we make sure to process the
615
- # last group.
616
- for i in range(1, len(results) + 1):
617
- if i == len(results):
618
- items = [''] * (prefix + 1)
619
- else:
620
- items = results[i].name.split('.')
621
- if items[prefix] != group_items[prefix]:
622
- title = '.'.join(group_items[:(prefix + 1)])
623
- callback(title, indent, results[group:i])
624
-
625
- print_tree(results[group:i], indent + 1, callback, end_callback)
626
-
627
- # Set the start of the next group to the current element.
628
- group = i
629
- group_items = items
630
-
631
- if end_callback:
632
- end_callback(indent)
633
-
634
-
635
- def print_sizes(sizes):
636
- """Prints the sizes in an indented format.
637
-
638
- Args:
639
- sizes: An array of FunctionSizes sorted on name.
640
- """
641
- # This callback is used to print the total sizes of each of the sub-groups.
642
- # Using the indent as padding allows to print a tree-like structure to
643
- # show how big each section is.
644
- def callback_factory(padding):
645
- # Use a factory so we capture the padding.
646
- def callback(title, indent, results):
647
- if title:
648
- size = sum([k.size for k in results])
649
- print('%s %*d %s' % (indent * ' ', padding, size, title))
650
- return callback
651
-
652
- total = sum([k.size for k in sizes])
653
- padding = int(math.ceil(math.log10(total)))
654
-
655
- print('%*d %s' % (padding, total, 'TOTAL'))
656
- print_tree(sizes, 0, callback_factory(padding), None)
657
-
658
-
659
- def print_deps(results, in_dot):
660
- """Prints the dependencies.
661
-
662
- Arguments:
663
- results: A sorted array of FunctionDependencies.
664
- in_dot: Whether to print in DOT format.
665
- """
666
- if not in_dot:
667
- for func in results:
668
- name, deps = func.name, func.deps
669
-
670
- # Ignore items with no dependencies.
671
- if deps:
672
- print(name)
673
-
674
- for dep in deps:
675
- print(' ' + dep)
676
-
677
- return
678
-
679
- dep_map = dict()
680
-
681
- # Use the printTree to produce clusters for each namespace and type. This
682
- # will print boxes around each class and show dependencies between types.
683
- print('digraph {')
684
- def callback_factory(dep_map, temp):
685
- """Creates a callback function."""
686
- def callback(title, indent, results):
687
- if title:
688
- if len(results) > 1:
689
- print('%s subgraph cluster%d {' % ('\t' * indent, len(temp)))
690
- temp.append(1)
691
- else:
692
- print('%s %d [label="%s"];' % ('\t' * indent, len(dep_map),
693
- results[0].name))
694
- dep_map[results[0].name] = len(dep_map)
695
-
696
- return callback
697
-
698
- def end_callback(indent):
699
- if indent > 1:
700
- print('\t' * (indent - 1), '}')
701
-
702
- print_tree(results, 1, callback_factory(dep_map, []), end_callback)
703
-
704
- for func in results:
705
- name, deps = func.name, func.deps
706
-
707
- # Ignore items with no dependencies.
708
- if deps:
709
- if name not in dep_map:
710
- dep_map[name] = len(dep_map)
711
- print('\t%s [label="%s"];' % (dep_map[name], name))
712
-
713
- for dep in deps:
714
- if dep not in dep_map:
715
- dep_map[dep] = len(dep_map)
716
- print('\t%s [label="%s"];' % (dep_map[dep], dep))
717
-
718
- print('\t%s -> %s;' % (dep_map[name], dep_map[dep]))
719
-
720
- print('}')
721
-
722
-
723
- def process(text, options):
724
- """Decodes a JSON string containing source map data.
725
-
726
- Args:
727
- text: A JSON string containing source map data.
728
- options: An object containing the command-line options.
729
- """
730
-
731
- # The spec allows a map file to start with )]} to prevent javascript from
732
- # including it.
733
- if text.startswith(')]}\'\n') or text.startswith(')]}\n'):
734
- _, text = text.split('\n', 1)
735
-
736
- # Decode the JSON data and get the parts we need.
737
- data = json.loads(text)
738
- # Paths are relative to the output directory.
739
- base = os.path.join(shakaBuildHelpers.get_source_base(), 'dist')
740
- with shakaBuildHelpers.open_file(os.path.join(base, data['file']), 'r') as f:
741
- file_lines = f.readlines()
742
- names = data['names']
743
- mappings = data['mappings']
744
- tokens = decode_mappings(mappings, names)
745
- sizes = process_sizes(tokens, file_lines)
746
-
747
- # Print out one of the results.
748
- if options.all_tokens:
749
- print_tokens(tokens, file_lines, sizes)
750
- elif options.function_sizes:
751
- print_sizes(sizes)
752
- elif options.function_deps or options.class_deps:
753
- temp = process_deps(tokens, file_lines, options.class_deps)
754
- print_deps(temp, options.dot_format)
755
-
756
-
757
- def main(args):
758
- parser = argparse.ArgumentParser(
759
- description=__doc__,
760
- formatter_class=argparse.RawDescriptionHelpFormatter)
761
-
762
- parser.add_argument('-d', '--dot-format', action='store_true',
763
- help='Prints in DOT format.')
764
- parser.add_argument('source_map', nargs='?',
765
- default='shaka-player.compiled.map',
766
- help='The source map or the name of the build to use.')
767
-
768
- print_types = parser.add_mutually_exclusive_group(required=True)
769
- print_types.add_argument('-c', '--class-deps', action='store_true',
770
- help='Prints the class dependencies.')
771
- print_types.add_argument('-f', '--function-deps', action='store_true',
772
- help='Prints the function dependencies.')
773
- print_types.add_argument(
774
- '-s', '--function-sizes', action='store_true',
775
- help='Prints the function sizes (in number of characters).')
776
- print_types.add_argument(
777
- '-t', '--all-tokens', action='store_true',
778
- help='Prints all tokens in the source map.')
779
-
780
- options = parser.parse_args(args)
781
-
782
- # Verify arguments are correct.
783
- if (options.dot_format and not options.function_deps
784
- and not options.class_deps):
785
- parser.error('--dot-format only valid with --function-deps or '
786
- '--class-deps.')
787
-
788
- # Try to find the file
789
- name = options.source_map
790
- if not os.path.isfile(name):
791
- # Get the source code base directory
792
- base = shakaBuildHelpers.get_source_base()
793
-
794
- # Supports the following searches:
795
- # * File name given, map in dist/
796
- # * Type given, map in working directory
797
- # * Type given, map in dist/
798
- if os.path.isfile(os.path.join(base, 'dist', name)):
799
- name = os.path.join(base, 'dist', name)
800
- elif os.path.isfile(
801
- os.path.join('shaka-player.' + name + '.debug.map')):
802
- name = os.path.join('shaka-player.' + name + '.debug.map')
803
- elif os.path.isfile(
804
- os.path.join(base, 'dist', 'shaka-player.' + name + '.debug.map')):
805
- name = os.path.join(base, 'dist', 'shaka-player.' + name + '.debug.map')
806
- else:
807
- logging.error('"%s" not found; build Shaka first.', name)
808
- return 1
809
-
810
- with shakaBuildHelpers.open_file(name, 'r') as f:
811
- process(f.read(), options)
812
- return 0
813
-
814
-
815
- if __name__ == '__main__':
816
- shakaBuildHelpers.run_main(main)