webrtc/examples/peerconnection/client/conductor.cc
Seth Hampson 13b8bad235 Final name changing of MediaStreamInterface.label() to id().
Downstreams have been updated, and this now updates all uses of label()
to id() within WebRTC code. This change also makes id() pure virtual and
removes label().

Bug: webrtc:8977
Change-Id: Ib045ea4fabba6f14447c64875c7aeba87dc2be24
Reviewed-on: https://webrtc-review.googlesource.com/60382
Reviewed-by: Per Kjellander <perkj@webrtc.org>
Reviewed-by: Taylor Brandstetter <deadbeef@webrtc.org>
Commit-Queue: Seth Hampson <shampson@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#22431}
2018-03-14 20:30:52 +00:00

567 lines
17 KiB
C++

/*
* Copyright 2012 The WebRTC Project Authors. All rights reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "examples/peerconnection/client/conductor.h"
#include <memory>
#include <utility>
#include <vector>
#include "api/audio_codecs/builtin_audio_decoder_factory.h"
#include "api/audio_codecs/builtin_audio_encoder_factory.h"
#include "api/test/fakeconstraints.h"
#include "examples/peerconnection/client/defaults.h"
#include "media/engine/webrtcvideocapturerfactory.h"
#include "modules/video_capture/video_capture_factory.h"
#include "rtc_base/checks.h"
#include "rtc_base/json.h"
#include "rtc_base/logging.h"
using webrtc::SdpType;
// Names used for a IceCandidate JSON object.
const char kCandidateSdpMidName[] = "sdpMid";
const char kCandidateSdpMlineIndexName[] = "sdpMLineIndex";
const char kCandidateSdpName[] = "candidate";
// Names used for a SessionDescription JSON object.
const char kSessionDescriptionTypeName[] = "type";
const char kSessionDescriptionSdpName[] = "sdp";
#define DTLS_ON true
#define DTLS_OFF false
class DummySetSessionDescriptionObserver
: public webrtc::SetSessionDescriptionObserver {
public:
static DummySetSessionDescriptionObserver* Create() {
return
new rtc::RefCountedObject<DummySetSessionDescriptionObserver>();
}
virtual void OnSuccess() { RTC_LOG(INFO) << __FUNCTION__; }
virtual void OnFailure(const std::string& error) {
RTC_LOG(INFO) << __FUNCTION__ << " " << error;
}
protected:
DummySetSessionDescriptionObserver() {}
~DummySetSessionDescriptionObserver() {}
};
Conductor::Conductor(PeerConnectionClient* client, MainWindow* main_wnd)
: peer_id_(-1),
loopback_(false),
client_(client),
main_wnd_(main_wnd) {
client_->RegisterObserver(this);
main_wnd->RegisterObserver(this);
}
Conductor::~Conductor() {
RTC_DCHECK(peer_connection_.get() == NULL);
}
bool Conductor::connection_active() const {
return peer_connection_.get() != NULL;
}
void Conductor::Close() {
client_->SignOut();
DeletePeerConnection();
}
bool Conductor::InitializePeerConnection() {
RTC_DCHECK(peer_connection_factory_.get() == NULL);
RTC_DCHECK(peer_connection_.get() == NULL);
peer_connection_factory_ = webrtc::CreatePeerConnectionFactory(
webrtc::CreateBuiltinAudioEncoderFactory(),
webrtc::CreateBuiltinAudioDecoderFactory());
if (!peer_connection_factory_.get()) {
main_wnd_->MessageBox("Error",
"Failed to initialize PeerConnectionFactory", true);
DeletePeerConnection();
return false;
}
if (!CreatePeerConnection(DTLS_ON)) {
main_wnd_->MessageBox("Error",
"CreatePeerConnection failed", true);
DeletePeerConnection();
}
AddStreams();
return peer_connection_.get() != NULL;
}
bool Conductor::ReinitializePeerConnectionForLoopback() {
loopback_ = true;
rtc::scoped_refptr<webrtc::StreamCollectionInterface> streams(
peer_connection_->local_streams());
peer_connection_ = NULL;
if (CreatePeerConnection(DTLS_OFF)) {
for (size_t i = 0; i < streams->count(); ++i)
peer_connection_->AddStream(streams->at(i));
peer_connection_->CreateOffer(this, NULL);
}
return peer_connection_.get() != NULL;
}
bool Conductor::CreatePeerConnection(bool dtls) {
RTC_DCHECK(peer_connection_factory_.get() != NULL);
RTC_DCHECK(peer_connection_.get() == NULL);
webrtc::PeerConnectionInterface::RTCConfiguration config;
webrtc::PeerConnectionInterface::IceServer server;
server.uri = GetPeerConnectionString();
config.servers.push_back(server);
webrtc::FakeConstraints constraints;
if (dtls) {
constraints.AddOptional(webrtc::MediaConstraintsInterface::kEnableDtlsSrtp,
"true");
} else {
constraints.AddOptional(webrtc::MediaConstraintsInterface::kEnableDtlsSrtp,
"false");
}
peer_connection_ = peer_connection_factory_->CreatePeerConnection(
config, &constraints, NULL, NULL, this);
return peer_connection_.get() != NULL;
}
void Conductor::DeletePeerConnection() {
peer_connection_ = NULL;
active_streams_.clear();
main_wnd_->StopLocalRenderer();
main_wnd_->StopRemoteRenderer();
peer_connection_factory_ = NULL;
peer_id_ = -1;
loopback_ = false;
}
void Conductor::EnsureStreamingUI() {
RTC_DCHECK(peer_connection_.get() != NULL);
if (main_wnd_->IsWindow()) {
if (main_wnd_->current_ui() != MainWindow::STREAMING)
main_wnd_->SwitchToStreamingUI();
}
}
//
// PeerConnectionObserver implementation.
//
// Called when a remote stream is added
void Conductor::OnAddStream(
rtc::scoped_refptr<webrtc::MediaStreamInterface> stream) {
RTC_LOG(INFO) << __FUNCTION__ << " " << stream->id();
main_wnd_->QueueUIThreadCallback(NEW_STREAM_ADDED, stream.release());
}
void Conductor::OnRemoveStream(
rtc::scoped_refptr<webrtc::MediaStreamInterface> stream) {
RTC_LOG(INFO) << __FUNCTION__ << " " << stream->id();
main_wnd_->QueueUIThreadCallback(STREAM_REMOVED, stream.release());
}
void Conductor::OnIceCandidate(const webrtc::IceCandidateInterface* candidate) {
RTC_LOG(INFO) << __FUNCTION__ << " " << candidate->sdp_mline_index();
// For loopback test. To save some connecting delay.
if (loopback_) {
if (!peer_connection_->AddIceCandidate(candidate)) {
RTC_LOG(WARNING) << "Failed to apply the received candidate";
}
return;
}
Json::StyledWriter writer;
Json::Value jmessage;
jmessage[kCandidateSdpMidName] = candidate->sdp_mid();
jmessage[kCandidateSdpMlineIndexName] = candidate->sdp_mline_index();
std::string sdp;
if (!candidate->ToString(&sdp)) {
RTC_LOG(LS_ERROR) << "Failed to serialize candidate";
return;
}
jmessage[kCandidateSdpName] = sdp;
SendMessage(writer.write(jmessage));
}
//
// PeerConnectionClientObserver implementation.
//
void Conductor::OnSignedIn() {
RTC_LOG(INFO) << __FUNCTION__;
main_wnd_->SwitchToPeerList(client_->peers());
}
void Conductor::OnDisconnected() {
RTC_LOG(INFO) << __FUNCTION__;
DeletePeerConnection();
if (main_wnd_->IsWindow())
main_wnd_->SwitchToConnectUI();
}
void Conductor::OnPeerConnected(int id, const std::string& name) {
RTC_LOG(INFO) << __FUNCTION__;
// Refresh the list if we're showing it.
if (main_wnd_->current_ui() == MainWindow::LIST_PEERS)
main_wnd_->SwitchToPeerList(client_->peers());
}
void Conductor::OnPeerDisconnected(int id) {
RTC_LOG(INFO) << __FUNCTION__;
if (id == peer_id_) {
RTC_LOG(INFO) << "Our peer disconnected";
main_wnd_->QueueUIThreadCallback(PEER_CONNECTION_CLOSED, NULL);
} else {
// Refresh the list if we're showing it.
if (main_wnd_->current_ui() == MainWindow::LIST_PEERS)
main_wnd_->SwitchToPeerList(client_->peers());
}
}
void Conductor::OnMessageFromPeer(int peer_id, const std::string& message) {
RTC_DCHECK(peer_id_ == peer_id || peer_id_ == -1);
RTC_DCHECK(!message.empty());
if (!peer_connection_.get()) {
RTC_DCHECK(peer_id_ == -1);
peer_id_ = peer_id;
if (!InitializePeerConnection()) {
RTC_LOG(LS_ERROR) << "Failed to initialize our PeerConnection instance";
client_->SignOut();
return;
}
} else if (peer_id != peer_id_) {
RTC_DCHECK(peer_id_ != -1);
RTC_LOG(WARNING)
<< "Received a message from unknown peer while already in a "
"conversation with a different peer.";
return;
}
Json::Reader reader;
Json::Value jmessage;
if (!reader.parse(message, jmessage)) {
RTC_LOG(WARNING) << "Received unknown message. " << message;
return;
}
std::string type_str;
std::string json_object;
rtc::GetStringFromJsonObject(jmessage, kSessionDescriptionTypeName,
&type_str);
if (!type_str.empty()) {
if (type_str == "offer-loopback") {
// This is a loopback call.
// Recreate the peerconnection with DTLS disabled.
if (!ReinitializePeerConnectionForLoopback()) {
RTC_LOG(LS_ERROR) << "Failed to initialize our PeerConnection instance";
DeletePeerConnection();
client_->SignOut();
}
return;
}
rtc::Optional<SdpType> type_maybe = webrtc::SdpTypeFromString(type_str);
if (!type_maybe) {
RTC_LOG(LS_ERROR) << "Unknown SDP type: " << type_str;
return;
}
SdpType type = *type_maybe;
std::string sdp;
if (!rtc::GetStringFromJsonObject(jmessage, kSessionDescriptionSdpName,
&sdp)) {
RTC_LOG(WARNING) << "Can't parse received session description message.";
return;
}
webrtc::SdpParseError error;
std::unique_ptr<webrtc::SessionDescriptionInterface> session_description =
webrtc::CreateSessionDescription(type, sdp, &error);
if (!session_description) {
RTC_LOG(WARNING) << "Can't parse received session description message. "
<< "SdpParseError was: " << error.description;
return;
}
RTC_LOG(INFO) << " Received session description :" << message;
peer_connection_->SetRemoteDescription(
DummySetSessionDescriptionObserver::Create(),
session_description.release());
if (type == SdpType::kOffer) {
peer_connection_->CreateAnswer(this, NULL);
}
return;
} else {
std::string sdp_mid;
int sdp_mlineindex = 0;
std::string sdp;
if (!rtc::GetStringFromJsonObject(jmessage, kCandidateSdpMidName,
&sdp_mid) ||
!rtc::GetIntFromJsonObject(jmessage, kCandidateSdpMlineIndexName,
&sdp_mlineindex) ||
!rtc::GetStringFromJsonObject(jmessage, kCandidateSdpName, &sdp)) {
RTC_LOG(WARNING) << "Can't parse received message.";
return;
}
webrtc::SdpParseError error;
std::unique_ptr<webrtc::IceCandidateInterface> candidate(
webrtc::CreateIceCandidate(sdp_mid, sdp_mlineindex, sdp, &error));
if (!candidate.get()) {
RTC_LOG(WARNING) << "Can't parse received candidate message. "
<< "SdpParseError was: " << error.description;
return;
}
if (!peer_connection_->AddIceCandidate(candidate.get())) {
RTC_LOG(WARNING) << "Failed to apply the received candidate";
return;
}
RTC_LOG(INFO) << " Received candidate :" << message;
return;
}
}
void Conductor::OnMessageSent(int err) {
// Process the next pending message if any.
main_wnd_->QueueUIThreadCallback(SEND_MESSAGE_TO_PEER, NULL);
}
void Conductor::OnServerConnectionFailure() {
main_wnd_->MessageBox("Error", ("Failed to connect to " + server_).c_str(),
true);
}
//
// MainWndCallback implementation.
//
void Conductor::StartLogin(const std::string& server, int port) {
if (client_->is_connected())
return;
server_ = server;
client_->Connect(server, port, GetPeerName());
}
void Conductor::DisconnectFromServer() {
if (client_->is_connected())
client_->SignOut();
}
void Conductor::ConnectToPeer(int peer_id) {
RTC_DCHECK(peer_id_ == -1);
RTC_DCHECK(peer_id != -1);
if (peer_connection_.get()) {
main_wnd_->MessageBox("Error",
"We only support connecting to one peer at a time", true);
return;
}
if (InitializePeerConnection()) {
peer_id_ = peer_id;
peer_connection_->CreateOffer(this, NULL);
} else {
main_wnd_->MessageBox("Error", "Failed to initialize PeerConnection", true);
}
}
std::unique_ptr<cricket::VideoCapturer> Conductor::OpenVideoCaptureDevice() {
std::vector<std::string> device_names;
{
std::unique_ptr<webrtc::VideoCaptureModule::DeviceInfo> info(
webrtc::VideoCaptureFactory::CreateDeviceInfo());
if (!info) {
return nullptr;
}
int num_devices = info->NumberOfDevices();
for (int i = 0; i < num_devices; ++i) {
const uint32_t kSize = 256;
char name[kSize] = {0};
char id[kSize] = {0};
if (info->GetDeviceName(i, name, kSize, id, kSize) != -1) {
device_names.push_back(name);
}
}
}
cricket::WebRtcVideoDeviceCapturerFactory factory;
std::unique_ptr<cricket::VideoCapturer> capturer;
for (const auto& name : device_names) {
capturer = factory.Create(cricket::Device(name, 0));
if (capturer) {
break;
}
}
return capturer;
}
void Conductor::AddStreams() {
if (active_streams_.find(kStreamId) != active_streams_.end())
return; // Already added.
rtc::scoped_refptr<webrtc::AudioTrackInterface> audio_track(
peer_connection_factory_->CreateAudioTrack(
kAudioLabel, peer_connection_factory_->CreateAudioSource(NULL)));
rtc::scoped_refptr<webrtc::VideoTrackInterface> video_track;
auto video_device(OpenVideoCaptureDevice());
if (video_device) {
video_track =
peer_connection_factory_->CreateVideoTrack(
kVideoLabel,
peer_connection_factory_->CreateVideoSource(std::move(video_device),
NULL));
main_wnd_->StartLocalRenderer(video_track);
} else {
RTC_LOG(LS_ERROR) << "OpenVideoCaptureDevice failed";
}
rtc::scoped_refptr<webrtc::MediaStreamInterface> stream =
peer_connection_factory_->CreateLocalMediaStream(kStreamId);
stream->AddTrack(audio_track);
if (video_track)
stream->AddTrack(video_track);
if (!peer_connection_->AddStream(stream)) {
RTC_LOG(LS_ERROR) << "Adding stream to PeerConnection failed";
}
typedef std::pair<std::string,
rtc::scoped_refptr<webrtc::MediaStreamInterface> >
MediaStreamPair;
active_streams_.insert(MediaStreamPair(stream->id(), stream));
main_wnd_->SwitchToStreamingUI();
}
void Conductor::DisconnectFromCurrentPeer() {
RTC_LOG(INFO) << __FUNCTION__;
if (peer_connection_.get()) {
client_->SendHangUp(peer_id_);
DeletePeerConnection();
}
if (main_wnd_->IsWindow())
main_wnd_->SwitchToPeerList(client_->peers());
}
void Conductor::UIThreadCallback(int msg_id, void* data) {
switch (msg_id) {
case PEER_CONNECTION_CLOSED:
RTC_LOG(INFO) << "PEER_CONNECTION_CLOSED";
DeletePeerConnection();
RTC_DCHECK(active_streams_.empty());
if (main_wnd_->IsWindow()) {
if (client_->is_connected()) {
main_wnd_->SwitchToPeerList(client_->peers());
} else {
main_wnd_->SwitchToConnectUI();
}
} else {
DisconnectFromServer();
}
break;
case SEND_MESSAGE_TO_PEER: {
RTC_LOG(INFO) << "SEND_MESSAGE_TO_PEER";
std::string* msg = reinterpret_cast<std::string*>(data);
if (msg) {
// For convenience, we always run the message through the queue.
// This way we can be sure that messages are sent to the server
// in the same order they were signaled without much hassle.
pending_messages_.push_back(msg);
}
if (!pending_messages_.empty() && !client_->IsSendingMessage()) {
msg = pending_messages_.front();
pending_messages_.pop_front();
if (!client_->SendToPeer(peer_id_, *msg) && peer_id_ != -1) {
RTC_LOG(LS_ERROR) << "SendToPeer failed";
DisconnectFromServer();
}
delete msg;
}
if (!peer_connection_.get())
peer_id_ = -1;
break;
}
case NEW_STREAM_ADDED: {
webrtc::MediaStreamInterface* stream =
reinterpret_cast<webrtc::MediaStreamInterface*>(
data);
webrtc::VideoTrackVector tracks = stream->GetVideoTracks();
// Only render the first track.
if (!tracks.empty()) {
webrtc::VideoTrackInterface* track = tracks[0];
main_wnd_->StartRemoteRenderer(track);
}
stream->Release();
break;
}
case STREAM_REMOVED: {
// Remote peer stopped sending a stream.
webrtc::MediaStreamInterface* stream =
reinterpret_cast<webrtc::MediaStreamInterface*>(
data);
stream->Release();
break;
}
default:
RTC_NOTREACHED();
break;
}
}
void Conductor::OnSuccess(webrtc::SessionDescriptionInterface* desc) {
peer_connection_->SetLocalDescription(
DummySetSessionDescriptionObserver::Create(), desc);
std::string sdp;
desc->ToString(&sdp);
// For loopback test. To save some connecting delay.
if (loopback_) {
// Replace message type from "offer" to "answer"
std::unique_ptr<webrtc::SessionDescriptionInterface> session_description =
webrtc::CreateSessionDescription(SdpType::kAnswer, sdp);
peer_connection_->SetRemoteDescription(
DummySetSessionDescriptionObserver::Create(),
session_description.release());
return;
}
Json::StyledWriter writer;
Json::Value jmessage;
jmessage[kSessionDescriptionTypeName] =
webrtc::SdpTypeToString(desc->GetType());
jmessage[kSessionDescriptionSdpName] = sdp;
SendMessage(writer.write(jmessage));
}
void Conductor::OnFailure(const std::string& error) {
RTC_LOG(LERROR) << error;
}
void Conductor::SendMessage(const std::string& json_object) {
std::string* msg = new std::string(json_object);
main_wnd_->QueueUIThreadCallback(SEND_MESSAGE_TO_PEER, msg);
}