1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
content / browser / resources / media / manager.js [blame]
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {$} from 'chrome://resources/js/util.js';
import {PlayerInfo} from './player_info.js';
import {objectForEach} from './util.js';
/**
* @fileoverview Keeps track of all the existing PlayerInfo and
* audio stream objects and is the entry-point for messages from the backend.
*
* The events captured by Manager (add, remove, update) are relayed
* to the clientRenderer which it can choose to use to modify the UI.
*/
export class Manager {
constructor(clientRenderer) {
this.players_ = {};
this.audioInfo_ = {};
this.audioComponents_ = [];
this.clientRenderer_ = clientRenderer;
const copyAllPlayerButton = $('copy-all-player-button');
const copyAllAudioButton = $('copy-all-audio-button');
const hidePlayersButton = $('hide-players-button');
// In tests we may not have these buttons.
if (copyAllPlayerButton) {
copyAllPlayerButton.onclick = function() {
this.clientRenderer_.renderClipboard(
JSON.stringify(this.players_, null, 2));
}.bind(this);
}
if (copyAllAudioButton) {
copyAllAudioButton.onclick = function() {
this.clientRenderer_.renderClipboard(
JSON.stringify(this.audioInfo_, null, 2) + '\n\n' +
JSON.stringify(this.audioComponents_, null, 2));
}.bind(this);
}
if (hidePlayersButton) {
hidePlayersButton.onclick = this.hidePlayers_.bind(this);
}
}
/**
* Updates the audio focus state.
* @param sessions A list of media sessions that contain the current state.
*/
updateAudioFocusSessions(sessions) {
this.clientRenderer_.audioFocusSessionUpdated(sessions);
}
/**
* Updates the registered CDM list.
* @param cdms A list of registered Content Decryption Modules.
*/
updateRegisteredCdms(cdms) {
this.clientRenderer_.updateRegisteredCdms(cdms);
}
/**
* Updates the general audio information.
* @param audioInfo The map of information.
*/
updateGeneralAudioInformation(audioInfo) {
this.audioInfo_ = audioInfo;
this.clientRenderer_.generalAudioInformationSet(this.audioInfo_);
}
/**
* Updates an audio-component.
* @param componentType Integer AudioComponent enum value; must match values
* from the AudioLogFactory::AudioComponent enum.
* @param componentId The unique-id of the audio-component.
* @param componentData The actual component data dictionary.
*/
updateAudioComponent(componentType, componentId, componentData) {
if (!(componentType in this.audioComponents_)) {
this.audioComponents_[componentType] = {};
}
if (!(componentId in this.audioComponents_[componentType])) {
this.audioComponents_[componentType][componentId] = componentData;
} else {
for (const key in componentData) {
this.audioComponents_[componentType][componentId][key] =
componentData[key];
}
}
this.clientRenderer_.audioComponentAdded(
componentType, this.audioComponents_[componentType]);
}
/**
* Removes an audio-stream from the manager.
* @param id The unique-id of the audio-stream.
*/
removeAudioComponent(componentType, componentId) {
if (!(componentType in this.audioComponents_) ||
!(componentId in this.audioComponents_[componentType])) {
return;
}
delete this.audioComponents_[componentType][componentId];
this.clientRenderer_.audioComponentRemoved(
componentType, this.audioComponents_[componentType]);
}
/**
* Adds a player to the list of players to manage.
*/
addPlayer(id) {
if (this.players_[id]) {
return;
}
// Make the PlayerProperty and add it to the mapping
this.players_[id] = new PlayerInfo(id);
this.clientRenderer_.playerAdded(this.players_, this.players_[id]);
}
/**
* Attempts to remove a player from the UI.
* @param id The ID of the player to remove.
*/
removePlayer(id) {
const playerRemoved = this.players_[id];
delete this.players_[id];
this.clientRenderer_.playerRemoved(this.players_, playerRemoved);
}
hidePlayers_() {
objectForEach(this.players_, function(playerInfo, id) {
this.removePlayer(id);
}, this);
}
updatePlayerInfoNoRecord(id, timestamp, key, value) {
if (!this.players_[id]) {
console.error('[updatePlayerInfo] Id ' + id + ' does not exist');
return;
}
this.players_[id].addPropertyNoRecord(timestamp, key, value);
this.clientRenderer_.playerUpdated(
this.players_, this.players_[id], key, value);
}
/**
*
* @param id The unique ID that identifies the player to be updated.
* @param timestamp The timestamp of when the change occurred. This
* timestamp is *not* normalized.
* @param key The name of the property to be added/changed.
* @param value The value of the property.
*/
updatePlayerInfo(id, timestamp, key, value) {
if (!this.players_[id]) {
console.error('[updatePlayerInfo] Id ' + id + ' does not exist');
return;
}
this.players_[id].addProperty(timestamp, key, value);
this.clientRenderer_.playerUpdated(
this.players_, this.players_[id], key, value);
}
parseVideoCaptureFormat_(format) {
/**
* Example:
*
* format:
* "(160x120)@30.000fps, pixel format: PIXEL_FORMAT_I420, storage: CPU"
*
* formatDict:
* {'resolution':'1280x720', 'fps': '30.00', "storage: "CPU" }
*/
const parts = format.split(', ');
const formatDict = {};
for (const i in parts) {
let kv = parts[i].split(': ');
if (kv.length === 2) {
if (kv[0] === 'pixel format') {
// The camera does not actually output I420,
// so this info is misleading.
continue;
}
formatDict[kv[0]] = kv[1];
} else {
kv = parts[i].split('@');
if (kv.length === 2) {
formatDict['resolution'] = kv[0].replace(/[)(]/g, '');
// Round down the FPS to 2 decimals.
formatDict['fps'] = parseFloat(kv[1].replace(/fps$/, '')).toFixed(2);
}
}
}
return formatDict;
}
updateVideoCaptureCapabilities(videoCaptureCapabilities) {
// Parse the video formats to be structured for the table.
for (const i in videoCaptureCapabilities) {
for (const j in videoCaptureCapabilities[i]['formats']) {
videoCaptureCapabilities[i]['formats'][j] =
this.parseVideoCaptureFormat_(
videoCaptureCapabilities[i]['formats'][j]);
}
videoCaptureCapabilities[i]['controlSupport'] =
videoCaptureCapabilities[i]['controlSupport'].join(' ') || 'N/A';
}
// The keys of each device to be shown in order of appearance.
const videoCaptureDeviceKeys =
['name', 'formats', 'captureApi', 'controlSupport', 'id'];
this.clientRenderer_.redrawVideoCaptureCapabilities(
videoCaptureCapabilities, videoCaptureDeviceKeys);
}
}
// Exporting the class on window for tests.
window.Manager = Manager;