OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
| 5 var media = {}; |
| 6 |
| 7 <include src="main.js"/> |
| 8 <include src="util.js"/> |
5 <include src="cache_entry.js"/> | 9 <include src="cache_entry.js"/> |
6 <include src="disjoint_range_set.js"/> | 10 <include src="disjoint_range_set.js"/> |
7 <include src="event_list.js"/> | 11 <include src="player_info.js"/> |
8 <include src="item_store.js"/> | 12 <include src="manager.js"/> |
9 <include src="media_player.js"/> | 13 <include src="client_renderer.js"/> |
10 <include src="metrics.js"/> | |
11 <include src="util.js"/> | |
12 | 14 |
13 cr.define('media', function() { | 15 media.initialize(new Manager(new ClientRenderer())); |
14 'use strict'; | |
15 | |
16 // Stores information on open audio streams, referenced by id. | |
17 var audioStreams = new media.ItemStore; | |
18 | |
19 // Active media players, indexed by 'render_id:player_id'. | |
20 var mediaPlayers = {}; | |
21 | |
22 // Cached files indexed by key and source id. | |
23 var cacheEntriesByKey = {}; | |
24 var cacheEntries = {}; | |
25 | |
26 // Map of event source -> url. | |
27 var requestURLs = {}; | |
28 | |
29 // Constants passed to us from Chrome. | |
30 var eventTypes = {}; | |
31 var eventPhases = {}; | |
32 | |
33 // The <div>s on the page in which to display information. | |
34 var audioStreamDiv; | |
35 var cacheDiv; | |
36 | |
37 // A timer used to limit the rate of redrawing the Media Players section. | |
38 var redrawTimer = null; | |
39 | |
40 /** | |
41 * Initialize variables and ask MediaInternals for all its data. | |
42 */ | |
43 function initialize() { | |
44 audioStreamDiv = $('audio-streams'); | |
45 cacheDiv = $('cache-entries'); | |
46 | |
47 // Get information about all currently active media. | |
48 chrome.send('getEverything'); | |
49 } | |
50 | |
51 /** | |
52 * Write the set of audio streams to the DOM. | |
53 */ | |
54 function printAudioStreams() { | |
55 | |
56 /** | |
57 * Render a single stream as a <li>. | |
58 * @param {Object} stream The stream to render. | |
59 * @return {HTMLElement} A <li> containing the stream information. | |
60 */ | |
61 function printStream(stream) { | |
62 var out = document.createElement('li'); | |
63 | |
64 function tableFromObject(obj) { | |
65 var table = document.createElement('table'); | |
66 for (var key in obj) { | |
67 var value = obj[key]; | |
68 var row = table.insertRow(-1); | |
69 var k = row.insertCell(-1); | |
70 var v = row.insertCell(-1); | |
71 k.appendChild(document.createTextNode(key)); | |
72 v.appendChild(document.createTextNode(value.toString())); | |
73 } | |
74 return table; | |
75 } | |
76 | |
77 out.appendChild(tableFromObject(stream)); | |
78 | |
79 return out; | |
80 } | |
81 | |
82 var out = document.createElement('ul'); | |
83 audioStreams.map(printStream).forEach(function(s) { | |
84 out.appendChild(s); | |
85 }); | |
86 | |
87 audioStreamDiv.textContent = ''; | |
88 audioStreamDiv.appendChild(out); | |
89 } | |
90 | |
91 /** | |
92 * Redraw each MediaPlayer. | |
93 */ | |
94 function printMediaPlayers() { | |
95 for (var key in mediaPlayers) { | |
96 mediaPlayers[key].redraw(); | |
97 } | |
98 redrawTimer = null; | |
99 } | |
100 | |
101 /** | |
102 * Write the set of sparse CacheEntries to the DOM. | |
103 */ | |
104 function printSparseCacheEntries() { | |
105 var out = document.createElement('ul'); | |
106 for (var key in cacheEntriesByKey) { | |
107 if (cacheEntriesByKey[key].sparse) | |
108 out.appendChild(cacheEntriesByKey[key].toListItem()); | |
109 } | |
110 | |
111 cacheDiv.textContent = ''; | |
112 cacheDiv.appendChild(out); | |
113 } | |
114 | |
115 /** | |
116 * Receiving data for an audio stream. | |
117 * Add it to audioStreams and update the page. | |
118 * @param {Object} stream JSON representation of an audio stream. | |
119 */ | |
120 function updateAudioStream(stream) { | |
121 audioStreams.addItem(stream); | |
122 printAudioStreams(); | |
123 } | |
124 | |
125 /** | |
126 * Receiving all data. | |
127 * Add it all to the appropriate stores and update the page. | |
128 * @param {Object} stuff JSON containing lists of data. | |
129 * @param {Object} stuff.audio_streams A dictionary of audio streams. | |
130 */ | |
131 function onReceiveEverything(stuff) { | |
132 audioStreams.addItems(stuff.audio_streams); | |
133 printAudioStreams(); | |
134 } | |
135 | |
136 /** | |
137 * Removing an item from the appropriate store. | |
138 * @param {string} id The id of the item to be removed, in the format | |
139 * "item_type.identifying_info". | |
140 */ | |
141 function onItemDeleted(id) { | |
142 var type = id.split('.')[0]; | |
143 switch (type) { | |
144 case 'audio_streams': | |
145 audioStreams.removeItem(id); | |
146 printAudioStreams(); | |
147 break; | |
148 } | |
149 } | |
150 | |
151 /** | |
152 * A render process has ended, delete any media players associated with it. | |
153 * @param {number} renderer The id of the render process. | |
154 */ | |
155 function onRendererTerminated(renderer) { | |
156 for (var key in mediaPlayers) { | |
157 if (mediaPlayers[key].renderer == renderer) { | |
158 $('media-players').removeChild(mediaPlayers[key]); | |
159 delete mediaPlayers[key]; | |
160 break; | |
161 } | |
162 } | |
163 printMediaPlayers(); | |
164 } | |
165 | |
166 /** | |
167 * Receiving net events. | |
168 * Update cache information and update that section of the page. | |
169 * @param {Array} updates A list of net events that have occurred. | |
170 */ | |
171 function onNetUpdate(updates) { | |
172 updates.forEach(function(update) { | |
173 var id = update.source.id; | |
174 if (!cacheEntries[id]) | |
175 cacheEntries[id] = new media.CacheEntry; | |
176 | |
177 switch (eventPhases[update.phase] + '.' + eventTypes[update.type]) { | |
178 case 'PHASE_BEGIN.DISK_CACHE_ENTRY_IMPL': | |
179 var key = update.params.key; | |
180 | |
181 // Merge this source with anything we already know about this key. | |
182 if (cacheEntriesByKey[key]) { | |
183 cacheEntriesByKey[key].merge(cacheEntries[id]); | |
184 cacheEntries[id] = cacheEntriesByKey[key]; | |
185 } else { | |
186 cacheEntriesByKey[key] = cacheEntries[id]; | |
187 } | |
188 cacheEntriesByKey[key].key = key; | |
189 break; | |
190 | |
191 case 'PHASE_BEGIN.SPARSE_READ': | |
192 cacheEntries[id].readBytes(update.params.offset, | |
193 update.params.buff_len); | |
194 cacheEntries[id].sparse = true; | |
195 break; | |
196 | |
197 case 'PHASE_BEGIN.SPARSE_WRITE': | |
198 cacheEntries[id].writeBytes(update.params.offset, | |
199 update.params.buff_len); | |
200 cacheEntries[id].sparse = true; | |
201 break; | |
202 | |
203 case 'PHASE_BEGIN.URL_REQUEST_START_JOB': | |
204 requestURLs[update.source.id] = update.params.url; | |
205 break; | |
206 | |
207 case 'PHASE_NONE.HTTP_TRANSACTION_READ_RESPONSE_HEADERS': | |
208 // Record the total size of the file if this was a range request. | |
209 var range = /content-range:\s*bytes\s*\d+-\d+\/(\d+)/i.exec( | |
210 update.params.headers); | |
211 var key = requestURLs[update.source.id]; | |
212 delete requestURLs[update.source.id]; | |
213 if (range && key) { | |
214 if (!cacheEntriesByKey[key]) { | |
215 cacheEntriesByKey[key] = new media.CacheEntry; | |
216 cacheEntriesByKey[key].key = key; | |
217 } | |
218 cacheEntriesByKey[key].size = range[1]; | |
219 } | |
220 break; | |
221 } | |
222 }); | |
223 | |
224 printSparseCacheEntries(); | |
225 } | |
226 | |
227 /** | |
228 * Receiving values for constants. Store them for later use. | |
229 * @param {Object} constants A dictionary of constants. | |
230 * @param {Object} constants.eventTypes A dictionary of event name -> int. | |
231 * @param {Object} constants.eventPhases A dictionary of event phase -> int. | |
232 */ | |
233 function onReceiveConstants(constants) { | |
234 var events = constants.eventTypes; | |
235 for (var e in events) { | |
236 eventTypes[events[e]] = e; | |
237 } | |
238 | |
239 var phases = constants.eventPhases; | |
240 for (var p in phases) { | |
241 eventPhases[phases[p]] = p; | |
242 } | |
243 } | |
244 | |
245 /** | |
246 * Receiving notification of a media event. | |
247 * @param {Object} event The json representation of a MediaLogEvent. | |
248 */ | |
249 function onMediaEvent(event) { | |
250 var source = event.renderer + ':' + event.player; | |
251 var item = mediaPlayers[source] || | |
252 new media.MediaPlayer({id: source, renderer: event.renderer}); | |
253 mediaPlayers[source] = item; | |
254 item.addEvent(event); | |
255 | |
256 // Both media and net events could provide the size of the file. | |
257 // Media takes priority, but keep the size in both places synchronized. | |
258 if (cacheEntriesByKey[item.properties.url]) { | |
259 item.properties.total_bytes = item.properties.total_bytes || | |
260 cacheEntriesByKey[item.properties.url].size; | |
261 cacheEntriesByKey[item.properties.url].size = item.properties.total_bytes; | |
262 } | |
263 | |
264 // Events tend to arrive in groups; don't redraw the page too often. | |
265 if (!redrawTimer) | |
266 redrawTimer = setTimeout(printMediaPlayers, 50); | |
267 } | |
268 | |
269 return { | |
270 initialize: initialize, | |
271 updateAudioStream: updateAudioStream, | |
272 cacheEntriesByKey: cacheEntriesByKey, | |
273 onReceiveEverything: onReceiveEverything, | |
274 onItemDeleted: onItemDeleted, | |
275 onRendererTerminated: onRendererTerminated, | |
276 onNetUpdate: onNetUpdate, | |
277 onReceiveConstants: onReceiveConstants, | |
278 onMediaEvent: onMediaEvent | |
279 }; | |
280 }); | |
281 | |
282 /** | |
283 * Initialize everything once we have access to the DOM. | |
284 */ | |
285 document.addEventListener('DOMContentLoaded', function() { | |
286 media.initialize(); | |
287 }); | |
OLD | NEW |