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 23namespace buzz { 24 25namespace { 26const char kPresenting[] = "s"; 27const char kNotPresenting[] = "o"; 28 29} // namespace 30 31// A simple serialiazer where presence of item => true, lack of item 32// => false. 33class 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 47class 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 105HangoutPubSubClient::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 181HangoutPubSubClient::~HangoutPubSubClient() { 182} 183 184void HangoutPubSubClient::RequestAll() { 185 presenter_client_->RequestItems(); 186 media_client_->RequestItems(); 187} 188 189void HangoutPubSubClient::OnPresenterRequestError( 190 PubSubClient* client, const XmlElement* stanza) { 191 SignalRequestError(client->node(), stanza); 192} 193 194void HangoutPubSubClient::OnMediaRequestError( 195 PubSubClient* client, const XmlElement* stanza) { 196 SignalRequestError(client->node(), stanza); 197} 198 199void HangoutPubSubClient::PublishPresenterState( 200 bool presenting, std::string* task_id_out) { 201 presenter_state_client_->Publish(nick_, presenting, task_id_out); 202} 203 204void HangoutPubSubClient::PublishAudioMuteState( 205 bool muted, std::string* task_id_out) { 206 audio_mute_state_client_->Publish(nick_, muted, task_id_out); 207} 208 209void HangoutPubSubClient::PublishVideoMuteState( 210 bool muted, std::string* task_id_out) { 211 video_mute_state_client_->Publish(nick_, muted, task_id_out); 212} 213 214void HangoutPubSubClient::PublishVideoPauseState( 215 bool paused, std::string* task_id_out) { 216 video_pause_state_client_->Publish(nick_, paused, task_id_out); 217} 218 219void 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. 225void 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. 232void 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 237void HangoutPubSubClient::OnPresenterStateChange( 238 const PubSubStateChange<bool>& change) { 239 SignalPresenterStateChange( 240 change.published_nick, change.old_state, change.new_state); 241} 242 243void HangoutPubSubClient::OnPresenterPublishResult( 244 const std::string& task_id, const XmlElement* item) { 245 SignalPublishPresenterResult(task_id); 246} 247 248void 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. 257void 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 278const 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 289const 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 300void 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 310void 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 321void HangoutPubSubClient::OnVideoMuteStateChange( 322 const PubSubStateChange<bool>& change) { 323 SignalVideoMuteStateChange( 324 change.published_nick, change.old_state, change.new_state); 325} 326 327void HangoutPubSubClient::OnVideoMutePublishResult( 328 const std::string& task_id, const XmlElement* item) { 329 SignalPublishVideoMuteResult(task_id); 330} 331 332void HangoutPubSubClient::OnVideoMutePublishError( 333 const std::string& task_id, const XmlElement* item, 334 const XmlElement* stanza) { 335 SignalPublishVideoMuteError(task_id, stanza); 336} 337 338void HangoutPubSubClient::OnVideoPauseStateChange( 339 const PubSubStateChange<bool>& change) { 340 SignalVideoPauseStateChange( 341 change.published_nick, change.old_state, change.new_state); 342} 343 344void HangoutPubSubClient::OnVideoPausePublishResult( 345 const std::string& task_id, const XmlElement* item) { 346 SignalPublishVideoPauseResult(task_id); 347} 348 349void HangoutPubSubClient::OnVideoPausePublishError( 350 const std::string& task_id, const XmlElement* item, 351 const XmlElement* stanza) { 352 SignalPublishVideoPauseError(task_id, stanza); 353} 354 355void HangoutPubSubClient::OnRecordingStateChange( 356 const PubSubStateChange<bool>& change) { 357 SignalRecordingStateChange( 358 change.published_nick, change.old_state, change.new_state); 359} 360 361void HangoutPubSubClient::OnRecordingPublishResult( 362 const std::string& task_id, const XmlElement* item) { 363 SignalPublishRecordingResult(task_id); 364} 365 366void HangoutPubSubClient::OnRecordingPublishError( 367 const std::string& task_id, const XmlElement* item, 368 const XmlElement* stanza) { 369 SignalPublishRecordingError(task_id, stanza); 370} 371 372void 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 387void 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 393void 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 401