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 |