OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright 2011 The WebRTC Project Authors. All rights reserved. | |
3 * | |
4 * Use of this source code is governed by a BSD-style license | |
5 * that can be found in the LICENSE file in the root of the source | |
6 * tree. An additional intellectual property rights grant can be found | |
7 * in the file PATENTS. All contributing project authors may | |
8 * be found in the AUTHORS file in the root of the source tree. | |
9 */ | |
10 | |
11 #include "webrtc/libjingle/xmpp/hangoutpubsubclient.h" | |
12 | |
13 #include "webrtc/libjingle/xmllite/qname.h" | |
14 #include "webrtc/libjingle/xmllite/xmlelement.h" | |
15 #include "webrtc/libjingle/xmpp/constants.h" | |
16 #include "webrtc/libjingle/xmpp/jid.h" | |
17 #include "webrtc/base/logging.h" | |
18 | |
19 | |
20 // Gives a high-level API for MUC call PubSub needs such as | |
21 // presenter state, recording state, mute state, and remote mute. | |
22 | |
23 namespace buzz { | |
24 | |
25 namespace { | |
26 const char kPresenting[] = "s"; | |
27 const char kNotPresenting[] = "o"; | |
28 | |
29 } // namespace | |
30 | |
31 // A simple serialiazer where presence of item => true, lack of item | |
32 // => false. | |
33 class BoolStateSerializer : public PubSubStateSerializer<bool> { | |
34 virtual XmlElement* Write(const QName& state_name, const bool& state) { | |
35 if (!state) { | |
36 return NULL; | |
37 } | |
38 | |
39 return new XmlElement(state_name, true); | |
40 } | |
41 | |
42 virtual void Parse(const XmlElement* state_elem, bool *state_out) { | |
43 *state_out = state_elem != NULL; | |
44 } | |
45 }; | |
46 | |
47 class PresenterStateClient : public PubSubStateClient<bool> { | |
48 public: | |
49 PresenterStateClient(const std::string& publisher_nick, | |
50 PubSubClient* client, | |
51 const QName& state_name, | |
52 bool default_state) | |
53 : PubSubStateClient<bool>( | |
54 publisher_nick, client, state_name, default_state, | |
55 new PublishedNickKeySerializer(), NULL) { | |
56 } | |
57 | |
58 virtual void Publish(const std::string& published_nick, | |
59 const bool& state, | |
60 std::string* task_id_out) { | |
61 XmlElement* presenter_elem = new XmlElement(QN_PRESENTER_PRESENTER, true); | |
62 presenter_elem->AddAttr(QN_NICK, published_nick); | |
63 | |
64 XmlElement* presentation_item_elem = | |
65 new XmlElement(QN_PRESENTER_PRESENTATION_ITEM, false); | |
66 const std::string& presentation_type = state ? kPresenting : kNotPresenting; | |
67 presentation_item_elem->AddAttr( | |
68 QN_PRESENTER_PRESENTATION_TYPE, presentation_type); | |
69 | |
70 // The Presenter state is kind of dumb in that it doesn't always use | |
71 // retracts. It relies on setting the "type" to a special value. | |
72 std::string itemid = published_nick; | |
73 std::vector<XmlElement*> children; | |
74 children.push_back(presenter_elem); | |
75 children.push_back(presentation_item_elem); | |
76 client()->PublishItem(itemid, children, task_id_out); | |
77 } | |
78 | |
79 protected: | |
80 virtual bool ParseStateItem(const PubSubItem& item, | |
81 StateItemInfo* info_out, | |
82 bool* state_out) { | |
83 const XmlElement* presenter_elem = | |
84 item.elem->FirstNamed(QN_PRESENTER_PRESENTER); | |
85 const XmlElement* presentation_item_elem = | |
86 item.elem->FirstNamed(QN_PRESENTER_PRESENTATION_ITEM); | |
87 if (presentation_item_elem == NULL || presenter_elem == NULL) { | |
88 return false; | |
89 } | |
90 | |
91 info_out->publisher_nick = | |
92 client()->GetPublisherNickFromPubSubItem(item.elem); | |
93 info_out->published_nick = presenter_elem->Attr(QN_NICK); | |
94 *state_out = (presentation_item_elem->Attr( | |
95 QN_PRESENTER_PRESENTATION_TYPE) != kNotPresenting); | |
96 return true; | |
97 } | |
98 | |
99 virtual bool StatesEqual(const bool& state1, const bool& state2) { | |
100 // Make every item trigger an event, even if state doesn't change. | |
101 return false; | |
102 } | |
103 }; | |
104 | |
105 HangoutPubSubClient::HangoutPubSubClient(XmppTaskParentInterface* parent, | |
106 const Jid& mucjid, | |
107 const std::string& nick) | |
108 : mucjid_(mucjid), | |
109 nick_(nick) { | |
110 presenter_client_.reset(new PubSubClient(parent, mucjid, NS_PRESENTER)); | |
111 presenter_client_->SignalRequestError.connect( | |
112 this, &HangoutPubSubClient::OnPresenterRequestError); | |
113 | |
114 media_client_.reset(new PubSubClient(parent, mucjid, NS_GOOGLE_MUC_MEDIA)); | |
115 media_client_->SignalRequestError.connect( | |
116 this, &HangoutPubSubClient::OnMediaRequestError); | |
117 | |
118 presenter_state_client_.reset(new PresenterStateClient( | |
119 nick_, presenter_client_.get(), QN_PRESENTER_PRESENTER, false)); | |
120 presenter_state_client_->SignalStateChange.connect( | |
121 this, &HangoutPubSubClient::OnPresenterStateChange); | |
122 presenter_state_client_->SignalPublishResult.connect( | |
123 this, &HangoutPubSubClient::OnPresenterPublishResult); | |
124 presenter_state_client_->SignalPublishError.connect( | |
125 this, &HangoutPubSubClient::OnPresenterPublishError); | |
126 | |
127 audio_mute_state_client_.reset(new PubSubStateClient<bool>( | |
128 nick_, media_client_.get(), QN_GOOGLE_MUC_AUDIO_MUTE, false, | |
129 new PublishedNickKeySerializer(), new BoolStateSerializer())); | |
130 // Can't just repeat because we need to watch for remote mutes. | |
131 audio_mute_state_client_->SignalStateChange.connect( | |
132 this, &HangoutPubSubClient::OnAudioMuteStateChange); | |
133 audio_mute_state_client_->SignalPublishResult.connect( | |
134 this, &HangoutPubSubClient::OnAudioMutePublishResult); | |
135 audio_mute_state_client_->SignalPublishError.connect( | |
136 this, &HangoutPubSubClient::OnAudioMutePublishError); | |
137 | |
138 video_mute_state_client_.reset(new PubSubStateClient<bool>( | |
139 nick_, media_client_.get(), QN_GOOGLE_MUC_VIDEO_MUTE, false, | |
140 new PublishedNickKeySerializer(), new BoolStateSerializer())); | |
141 // Can't just repeat because we need to watch for remote mutes. | |
142 video_mute_state_client_->SignalStateChange.connect( | |
143 this, &HangoutPubSubClient::OnVideoMuteStateChange); | |
144 video_mute_state_client_->SignalPublishResult.connect( | |
145 this, &HangoutPubSubClient::OnVideoMutePublishResult); | |
146 video_mute_state_client_->SignalPublishError.connect( | |
147 this, &HangoutPubSubClient::OnVideoMutePublishError); | |
148 | |
149 video_pause_state_client_.reset(new PubSubStateClient<bool>( | |
150 nick_, media_client_.get(), QN_GOOGLE_MUC_VIDEO_PAUSE, false, | |
151 new PublishedNickKeySerializer(), new BoolStateSerializer())); | |
152 video_pause_state_client_->SignalStateChange.connect( | |
153 this, &HangoutPubSubClient::OnVideoPauseStateChange); | |
154 video_pause_state_client_->SignalPublishResult.connect( | |
155 this, &HangoutPubSubClient::OnVideoPausePublishResult); | |
156 video_pause_state_client_->SignalPublishError.connect( | |
157 this, &HangoutPubSubClient::OnVideoPausePublishError); | |
158 | |
159 recording_state_client_.reset(new PubSubStateClient<bool>( | |
160 nick_, media_client_.get(), QN_GOOGLE_MUC_RECORDING, false, | |
161 new PublishedNickKeySerializer(), new BoolStateSerializer())); | |
162 recording_state_client_->SignalStateChange.connect( | |
163 this, &HangoutPubSubClient::OnRecordingStateChange); | |
164 recording_state_client_->SignalPublishResult.connect( | |
165 this, &HangoutPubSubClient::OnRecordingPublishResult); | |
166 recording_state_client_->SignalPublishError.connect( | |
167 this, &HangoutPubSubClient::OnRecordingPublishError); | |
168 | |
169 media_block_state_client_.reset(new PubSubStateClient<bool>( | |
170 nick_, media_client_.get(), QN_GOOGLE_MUC_MEDIA_BLOCK, false, | |
171 new PublisherAndPublishedNicksKeySerializer(), | |
172 new BoolStateSerializer())); | |
173 media_block_state_client_->SignalStateChange.connect( | |
174 this, &HangoutPubSubClient::OnMediaBlockStateChange); | |
175 media_block_state_client_->SignalPublishResult.connect( | |
176 this, &HangoutPubSubClient::OnMediaBlockPublishResult); | |
177 media_block_state_client_->SignalPublishError.connect( | |
178 this, &HangoutPubSubClient::OnMediaBlockPublishError); | |
179 } | |
180 | |
181 HangoutPubSubClient::~HangoutPubSubClient() { | |
182 } | |
183 | |
184 void HangoutPubSubClient::RequestAll() { | |
185 presenter_client_->RequestItems(); | |
186 media_client_->RequestItems(); | |
187 } | |
188 | |
189 void HangoutPubSubClient::OnPresenterRequestError( | |
190 PubSubClient* client, const XmlElement* stanza) { | |
191 SignalRequestError(client->node(), stanza); | |
192 } | |
193 | |
194 void HangoutPubSubClient::OnMediaRequestError( | |
195 PubSubClient* client, const XmlElement* stanza) { | |
196 SignalRequestError(client->node(), stanza); | |
197 } | |
198 | |
199 void HangoutPubSubClient::PublishPresenterState( | |
200 bool presenting, std::string* task_id_out) { | |
201 presenter_state_client_->Publish(nick_, presenting, task_id_out); | |
202 } | |
203 | |
204 void HangoutPubSubClient::PublishAudioMuteState( | |
205 bool muted, std::string* task_id_out) { | |
206 audio_mute_state_client_->Publish(nick_, muted, task_id_out); | |
207 } | |
208 | |
209 void HangoutPubSubClient::PublishVideoMuteState( | |
210 bool muted, std::string* task_id_out) { | |
211 video_mute_state_client_->Publish(nick_, muted, task_id_out); | |
212 } | |
213 | |
214 void HangoutPubSubClient::PublishVideoPauseState( | |
215 bool paused, std::string* task_id_out) { | |
216 video_pause_state_client_->Publish(nick_, paused, task_id_out); | |
217 } | |
218 | |
219 void HangoutPubSubClient::PublishRecordingState( | |
220 bool recording, std::string* task_id_out) { | |
221 recording_state_client_->Publish(nick_, recording, task_id_out); | |
222 } | |
223 | |
224 // Remote mute is accomplished by setting another client's mute state. | |
225 void HangoutPubSubClient::RemoteMute( | |
226 const std::string& mutee_nick, std::string* task_id_out) { | |
227 audio_mute_state_client_->Publish(mutee_nick, true, task_id_out); | |
228 } | |
229 | |
230 // Block media is accomplished by setting another client's block | |
231 // state, kind of like remote mute. | |
232 void HangoutPubSubClient::BlockMedia( | |
233 const std::string& blockee_nick, std::string* task_id_out) { | |
234 media_block_state_client_->Publish(blockee_nick, true, task_id_out); | |
235 } | |
236 | |
237 void HangoutPubSubClient::OnPresenterStateChange( | |
238 const PubSubStateChange<bool>& change) { | |
239 SignalPresenterStateChange( | |
240 change.published_nick, change.old_state, change.new_state); | |
241 } | |
242 | |
243 void HangoutPubSubClient::OnPresenterPublishResult( | |
244 const std::string& task_id, const XmlElement* item) { | |
245 SignalPublishPresenterResult(task_id); | |
246 } | |
247 | |
248 void HangoutPubSubClient::OnPresenterPublishError( | |
249 const std::string& task_id, const XmlElement* item, | |
250 const XmlElement* stanza) { | |
251 SignalPublishPresenterError(task_id, stanza); | |
252 } | |
253 | |
254 // Since a remote mute is accomplished by another client setting our | |
255 // mute state, if our state changes to muted, we should mute ourselves. | |
256 // Note that remote un-muting is disallowed by the RoomServer. | |
257 void HangoutPubSubClient::OnAudioMuteStateChange( | |
258 const PubSubStateChange<bool>& change) { | |
259 bool was_muted = change.old_state; | |
260 bool is_muted = change.new_state; | |
261 bool remote_action = (!change.publisher_nick.empty() && | |
262 (change.publisher_nick != change.published_nick)); | |
263 | |
264 if (remote_action) { | |
265 const std::string& mutee_nick = change.published_nick; | |
266 const std::string& muter_nick = change.publisher_nick; | |
267 if (!is_muted) { | |
268 // The server should prevent remote un-mute. | |
269 LOG(LS_WARNING) << muter_nick << " remote unmuted " << mutee_nick; | |
270 return; | |
271 } | |
272 bool should_mute_locally = (mutee_nick == nick_); | |
273 SignalRemoteMute(mutee_nick, muter_nick, should_mute_locally); | |
274 } | |
275 SignalAudioMuteStateChange(change.published_nick, was_muted, is_muted); | |
276 } | |
277 | |
278 const std::string GetAudioMuteNickFromItem(const XmlElement* item) { | |
279 if (item != NULL) { | |
280 const XmlElement* audio_mute_state = | |
281 item->FirstNamed(QN_GOOGLE_MUC_AUDIO_MUTE); | |
282 if (audio_mute_state != NULL) { | |
283 return audio_mute_state->Attr(QN_NICK); | |
284 } | |
285 } | |
286 return std::string(); | |
287 } | |
288 | |
289 const std::string GetBlockeeNickFromItem(const XmlElement* item) { | |
290 if (item != NULL) { | |
291 const XmlElement* media_block_state = | |
292 item->FirstNamed(QN_GOOGLE_MUC_MEDIA_BLOCK); | |
293 if (media_block_state != NULL) { | |
294 return media_block_state->Attr(QN_NICK); | |
295 } | |
296 } | |
297 return std::string(); | |
298 } | |
299 | |
300 void HangoutPubSubClient::OnAudioMutePublishResult( | |
301 const std::string& task_id, const XmlElement* item) { | |
302 const std::string& mutee_nick = GetAudioMuteNickFromItem(item); | |
303 if (mutee_nick != nick_) { | |
304 SignalRemoteMuteResult(task_id, mutee_nick); | |
305 } else { | |
306 SignalPublishAudioMuteResult(task_id); | |
307 } | |
308 } | |
309 | |
310 void HangoutPubSubClient::OnAudioMutePublishError( | |
311 const std::string& task_id, const XmlElement* item, | |
312 const XmlElement* stanza) { | |
313 const std::string& mutee_nick = GetAudioMuteNickFromItem(item); | |
314 if (mutee_nick != nick_) { | |
315 SignalRemoteMuteError(task_id, mutee_nick, stanza); | |
316 } else { | |
317 SignalPublishAudioMuteError(task_id, stanza); | |
318 } | |
319 } | |
320 | |
321 void HangoutPubSubClient::OnVideoMuteStateChange( | |
322 const PubSubStateChange<bool>& change) { | |
323 SignalVideoMuteStateChange( | |
324 change.published_nick, change.old_state, change.new_state); | |
325 } | |
326 | |
327 void HangoutPubSubClient::OnVideoMutePublishResult( | |
328 const std::string& task_id, const XmlElement* item) { | |
329 SignalPublishVideoMuteResult(task_id); | |
330 } | |
331 | |
332 void HangoutPubSubClient::OnVideoMutePublishError( | |
333 const std::string& task_id, const XmlElement* item, | |
334 const XmlElement* stanza) { | |
335 SignalPublishVideoMuteError(task_id, stanza); | |
336 } | |
337 | |
338 void HangoutPubSubClient::OnVideoPauseStateChange( | |
339 const PubSubStateChange<bool>& change) { | |
340 SignalVideoPauseStateChange( | |
341 change.published_nick, change.old_state, change.new_state); | |
342 } | |
343 | |
344 void HangoutPubSubClient::OnVideoPausePublishResult( | |
345 const std::string& task_id, const XmlElement* item) { | |
346 SignalPublishVideoPauseResult(task_id); | |
347 } | |
348 | |
349 void HangoutPubSubClient::OnVideoPausePublishError( | |
350 const std::string& task_id, const XmlElement* item, | |
351 const XmlElement* stanza) { | |
352 SignalPublishVideoPauseError(task_id, stanza); | |
353 } | |
354 | |
355 void HangoutPubSubClient::OnRecordingStateChange( | |
356 const PubSubStateChange<bool>& change) { | |
357 SignalRecordingStateChange( | |
358 change.published_nick, change.old_state, change.new_state); | |
359 } | |
360 | |
361 void HangoutPubSubClient::OnRecordingPublishResult( | |
362 const std::string& task_id, const XmlElement* item) { | |
363 SignalPublishRecordingResult(task_id); | |
364 } | |
365 | |
366 void HangoutPubSubClient::OnRecordingPublishError( | |
367 const std::string& task_id, const XmlElement* item, | |
368 const XmlElement* stanza) { | |
369 SignalPublishRecordingError(task_id, stanza); | |
370 } | |
371 | |
372 void HangoutPubSubClient::OnMediaBlockStateChange( | |
373 const PubSubStateChange<bool>& change) { | |
374 const std::string& blockee_nick = change.published_nick; | |
375 const std::string& blocker_nick = change.publisher_nick; | |
376 | |
377 bool was_blockee = change.old_state; | |
378 bool is_blockee = change.new_state; | |
379 if (!was_blockee && is_blockee) { | |
380 SignalMediaBlock(blockee_nick, blocker_nick); | |
381 } | |
382 // TODO: Should we bother signaling unblock? Currently | |
383 // it isn't allowed, but it might happen when a participant leaves | |
384 // the room and the item is retracted. | |
385 } | |
386 | |
387 void HangoutPubSubClient::OnMediaBlockPublishResult( | |
388 const std::string& task_id, const XmlElement* item) { | |
389 const std::string& blockee_nick = GetBlockeeNickFromItem(item); | |
390 SignalMediaBlockResult(task_id, blockee_nick); | |
391 } | |
392 | |
393 void HangoutPubSubClient::OnMediaBlockPublishError( | |
394 const std::string& task_id, const XmlElement* item, | |
395 const XmlElement* stanza) { | |
396 const std::string& blockee_nick = GetBlockeeNickFromItem(item); | |
397 SignalMediaBlockError(task_id, blockee_nick, stanza); | |
398 } | |
399 | |
400 } // namespace buzz | |
OLD | NEW |