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