/include | |||||
/include/webrtc | |||||
/libs | /libs | ||||
/depot_tools | /depot_tools | ||||
/webrtc-checkout | /webrtc-checkout | ||||
/build |
WEBRTC_DIR ?= . | |||||
WEBRTC_BASE ?= . | |||||
WEBRTC_DIR ?= $(WEBRTC_BASE) | |||||
WEBRTC_VERSION ?= m79 | WEBRTC_VERSION ?= m79 | ||||
WEBRTC_OUT ?= Release | WEBRTC_OUT ?= Release | ||||
WEBRTC_GN_ARGS ?= \ | WEBRTC_GN_ARGS ?= \ | ||||
WEBRTC_TARGETS ?= default $(WEBRTC_TARGETS_EXTRA) | WEBRTC_TARGETS ?= default $(WEBRTC_TARGETS_EXTRA) | ||||
WEBRTC_LIBS ?= libwebrtc.a $(WEBRTC_LIBS_EXTRA) | WEBRTC_LIBS ?= libwebrtc.a $(WEBRTC_LIBS_EXTRA) | ||||
WEBRTC_INCLUDES = \ | WEBRTC_INCLUDES = \ | ||||
api audio base common_audio common_video logging media modules p2p pc \ | |||||
rtc_base rtc_tools video common_types.h \ | |||||
api audio base call common_audio common_video logging media modules p2p pc \ | |||||
system_wrappers rtc_base rtc_tools video third_party common_types.h \ | |||||
$(WEBRTC_INCLUDES_EXTRA) | $(WEBRTC_INCLUDES_EXTRA) | ||||
SETPATH = PATH=$(WEBRTC_DIR)/depot_tools:"$$PATH" | |||||
WEBRTC_PLATFORM ?= POSIX | |||||
WEBRTC_FLAGS ?= \ | |||||
-I$(WEBRTC_BASE)/include -I$(WEBRTC_BASE)/include/webrtc \ | |||||
-I$(WEBRTC_BASE)/include/webrtc/third_party/abseil-cpp \ | |||||
-DWEBRTC_$(WEBRTC_PLATFORM) | |||||
WEBRTC_SETPATH = PATH=$(WEBRTC_DIR)/depot_tools:"$$PATH" | |||||
RTC_STAMP_DEPOT = $(WEBRTC_DIR)/depot_tools/.stamp | RTC_STAMP_DEPOT = $(WEBRTC_DIR)/depot_tools/.stamp | ||||
RTC_STAMP_REPO = $(WEBRTC_DIR)/webrtc-checkout/.stamp | RTC_STAMP_REPO = $(WEBRTC_DIR)/webrtc-checkout/.stamp | ||||
RTC_STAMP_VERSION = $(WEBRTC_DIR)/webrtc-checkout/.stamps/stamp-$(WEBRTC_VERSION) | RTC_STAMP_VERSION = $(WEBRTC_DIR)/webrtc-checkout/.stamps/stamp-$(WEBRTC_VERSION) | ||||
RTC_STAMP_COMPILE = $(WEBRTC_DIR)/webrtc-checkout/src/out/$(WEBRTC_OUT)/.stamp | RTC_STAMP_COMPILE = $(WEBRTC_DIR)/webrtc-checkout/src/out/$(WEBRTC_OUT)/.stamp | ||||
RTC_STAMP_INCLUDES = include/webrtc/.stamp | |||||
RTC_STAMP_INCLUDES = $(WEBRTC_BASE)/include/webrtc/.stamp | |||||
all: include/webrtc/.stamp $(patsubst %,libs/%,$(WEBRTC_LIBS)) | |||||
.PHONY: rtcall | |||||
rtcall: | |||||
$(WEBRTC_BASE)/include/webrtc/.stamp \ | |||||
$(patsubst %,$(WEBRTC_BASE)/libs/%,$(WEBRTC_LIBS)) \ | |||||
$(WEBRTC_BASE)/libs/libutil.a | |||||
# Get depot_tools, which contains all the tools required to work with Google software | # Get depot_tools, which contains all the tools required to work with Google software | ||||
$(RTC_STAMP_DEPOT): | $(RTC_STAMP_DEPOT): | ||||
rm -rf $(WEBRTC_DIR)/webrtc-checkout && mkdir $(WEBRTC_DIR)/webrtc-checkout | rm -rf $(WEBRTC_DIR)/webrtc-checkout && mkdir $(WEBRTC_DIR)/webrtc-checkout | ||||
# | # | ||||
# Fetch webrtc, thit will take time... | # Fetch webrtc, thit will take time... | ||||
cd $(WEBRTC_DIR)/webrtc-checkout && $(SETPATH) fetch --nohooks --no-history webrtc | |||||
cd $(WEBRTC_DIR)/webrtc-checkout && $(WEBRTC_SETPATH) fetch --nohooks --no-history webrtc | |||||
touch $@ | touch $@ | ||||
# Check out the correct WebRTC branch/commit | # Check out the correct WebRTC branch/commit | ||||
cd $(WEBRTC_DIR)/webrtc-checkout/src && git checkout branch-heads/$(WEBRTC_VERSION) | cd $(WEBRTC_DIR)/webrtc-checkout/src && git checkout branch-heads/$(WEBRTC_VERSION) | ||||
# | # | ||||
# Sync webrtc, this will take time... | # Sync webrtc, this will take time... | ||||
cd $(WEBRTC_DIR)/webrtc-checkout/src && $(SETPATH) gclient sync -D --nohooks | |||||
cd $(WEBRTC_DIR)/webrtc-checkout/src && $(WEBRTC_SETPATH) gclient sync -D --nohooks | |||||
touch $@ | touch $@ | ||||
# Compile | # Compile | ||||
$(RTC_STAMP_COMPILE): | $(RTC_STAMP_COMPILE): | ||||
cd $(WEBRTC_DIR)/webrtc-checkout/src && $(SETPATH) gn gen out/$(WEBRTC_OUT) --args='$(WEBRTC_GN_ARGS)' | |||||
cd $(WEBRTC_DIR)/webrtc-checkout/src && $(SETPATH) ninja -C out/$(WEBRTC_OUT) $(WEBRTC_TARGETS) | |||||
cd $(WEBRTC_DIR)/webrtc-checkout/src && $(WEBRTC_SETPATH) gn gen out/$(WEBRTC_OUT) --args='$(WEBRTC_GN_ARGS)' | |||||
cd $(WEBRTC_DIR)/webrtc-checkout/src && $(WEBRTC_SETPATH) ninja -C out/$(WEBRTC_OUT) $(WEBRTC_TARGETS) | |||||
touch $@ | touch $@ | ||||
# Includes | # Includes | ||||
touch $@ | touch $@ | ||||
# Libraries | # Libraries | ||||
libs/%.a: $(WEBRTC_DIR)/webrtc-checkout/src/out/$(WEBRTC_OUT)/obj/%.a | |||||
$(WEBRTC_BASE)/libs/%.a: $(WEBRTC_DIR)/webrtc-checkout/src/out/$(WEBRTC_OUT)/obj/%.a | |||||
@mkdir -p $(@D) | @mkdir -p $(@D) | ||||
cp $< $@ | cp $< $@ | ||||
.PHONY: clean | |||||
clean: | |||||
# Util | |||||
$(WEBRTC_BASE)/libs/libutil.a: $(WEBRTC_BASE)/build/util/Conductor.cc.o | |||||
@mkdir -p $(@D) | |||||
ar cr $@ $^ | |||||
$(WEBRTC_BASE)/build/%.cc.o: $(WEBRTC_BASE)/%.cc $(RTC_STAMP_INCLUDES) $(wildcard $(WEBRTC_BASE)/util/*.h) | |||||
@mkdir -p $(@D) | |||||
$(CXX) $(CXXFLAGS) $(WEBRTC_FLAGS) -c $< -o $@ | |||||
.PHONY: rtcclean | |||||
rtcclean: | |||||
rm -rf include libs build | |||||
.PHONY: rtccleanall | |||||
rtccleanall: rtcclean | |||||
rm -rf $(WEBRTC_DIR)/depot_tools $(WEBRTC_DIR)/webrtc-checkout | rm -rf $(WEBRTC_DIR)/depot_tools $(WEBRTC_DIR)/webrtc-checkout | ||||
rm -rf include libs | |||||
cd $(WEBRTC_DIR)/webrtc-checkout/src && $(WEBRTC_SETPATH) gn clean out/$(WEBRTC_OUT) | |||||
.PHONY: gn-clean | |||||
gn-clean: | |||||
cd $(WEBRTC_DIR)/webrtc-checkout/src && $(SETPATH) gn clean out/$(WEBRTC_OUT) | |||||
.PHONY: mockstamps | |||||
rtcmockstamps: | |||||
mkdir -p $(dir $(RTC_STAMP_DEPOT)) && touch $(RTC_STAMP_DEPOT) | |||||
mkdir -p $(dir $(RTC_STAMP_REPO)) && touch $(RTC_STAMP_REPO) | |||||
mkdir -p $(dir $(RTC_STAMP_VERSION)) && touch $(RTC_STAMP_VERSION) | |||||
mkdir -p $(dir $(RTC_STAMP_COMPILE)) && touch $(RTC_STAMP_COMPILE) | |||||
mkdir -p $(dir $(RTC_STAMP_INCLUDES)) && touch $(RTC_STAMP_INCLUDES) |
#include <webrtc/api/peer_connection_interface.h> | |||||
#include <memory> | |||||
namespace rtcutil { | |||||
class ConductorObserver { | |||||
public: | |||||
enum class ErrorType { | |||||
WARNING, | |||||
RECEIVE_OFFER, | |||||
RECEIVE_ICE_CANDIDATE, | |||||
CREATE_ANSWER, | |||||
}; | |||||
virtual void onError(ErrorType type, std::string err); | |||||
virtual void onIceCandidate(std::string mid, int index, std::string sdp) = 0; | |||||
virtual void onAnswer(std::string sdp) = 0; | |||||
virtual void onRemoteTrack(rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> track); | |||||
virtual void removeRemoteTrack(rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> track); | |||||
}; | |||||
class Conductor: | |||||
public webrtc::PeerConnectionObserver, | |||||
public webrtc::CreateSessionDescriptionObserver { | |||||
public: | |||||
Conductor(ConductorObserver &observer): observer_(observer) {} | |||||
void receiveOffer(const std::string &sdp); | |||||
void receiveOffer(std::unique_ptr<webrtc::SessionDescriptionInterface> offer); | |||||
void receiveIceCandidate(std::string mid, int index, std::string sdp); | |||||
void ensurePeerConnection(); | |||||
protected: | |||||
// PeerConnectionObserver | |||||
void OnAddTrack( | |||||
rtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver, | |||||
const std::vector<rtc::scoped_refptr<webrtc::MediaStreamInterface>> &streams) override; | |||||
void OnRemoveTrack( | |||||
rtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver) override; | |||||
void OnIceCandidate(const webrtc::IceCandidateInterface *candidate) override; | |||||
void OnSignalingChange( | |||||
webrtc::PeerConnectionInterface::SignalingState new_state) override {} | |||||
void OnDataChannel( | |||||
rtc::scoped_refptr<webrtc::DataChannelInterface> channel) override {} | |||||
void OnRenegotiationNeeded() override {} | |||||
void OnIceConnectionChange( | |||||
webrtc::PeerConnectionInterface::IceConnectionState new_state) override {} | |||||
void OnIceGatheringChange( | |||||
webrtc::PeerConnectionInterface::IceGatheringState new_state) override {} | |||||
void OnIceConnectionReceivingChange(bool receiving) override {} | |||||
// CreateSessionDescriptionObserver | |||||
void OnSuccess(webrtc::SessionDescriptionInterface* desc) override; | |||||
void OnFailure(webrtc::RTCError error) override; | |||||
// Overridables for consumers | |||||
virtual rtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> | |||||
doCreatePeerConnectionFactory(); | |||||
virtual rtc::scoped_refptr<webrtc::PeerConnectionInterface> | |||||
doCreatePeerConnection(webrtc::PeerConnectionFactoryInterface &factory); | |||||
virtual void doAddTracks(webrtc::PeerConnectionInterface &conn) {}; | |||||
ConductorObserver &observer_; | |||||
rtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> peer_conn_factory_; | |||||
rtc::scoped_refptr<webrtc::PeerConnectionInterface> peer_conn_; | |||||
}; | |||||
} |
#include "Conductor.h" | |||||
#include <webrtc/api/create_peerconnection_factory.h> | |||||
#include <webrtc/api/audio_codecs/builtin_audio_encoder_factory.h> | |||||
#include <webrtc/api/audio_codecs/builtin_audio_decoder_factory.h> | |||||
#include <webrtc/api/video_codecs/builtin_video_encoder_factory.h> | |||||
#include <webrtc/api/video_codecs/builtin_video_decoder_factory.h> | |||||
#include <webrtc/pc/video_track_source.h> | |||||
#include <stdio.h> | |||||
#include <stdlib.h> | |||||
namespace rtcutil { | |||||
void ConductorObserver::onError(ErrorType type, std::string err) { | |||||
if (type == ErrorType::WARNING) { | |||||
fprintf(stderr, "Warning: %s\n", err.c_str()); | |||||
} else { | |||||
fprintf(stderr, "Error: %s\n", err.c_str()); | |||||
abort(); | |||||
} | |||||
} | |||||
class SetSessionDescriptionObserver: public webrtc::SetSessionDescriptionObserver { | |||||
public: | |||||
SetSessionDescriptionObserver(ConductorObserver &observer): observer_(observer) {} | |||||
static SetSessionDescriptionObserver* Create(ConductorObserver &observer) { | |||||
return new rtc::RefCountedObject<SetSessionDescriptionObserver>(observer); | |||||
} | |||||
virtual void OnSuccess() {} | |||||
virtual void OnFailure(webrtc::RTCError error) { | |||||
observer_.onError(ConductorObserver::ErrorType::RECEIVE_OFFER, | |||||
error.message()); | |||||
} | |||||
private: | |||||
ConductorObserver &observer_; | |||||
}; | |||||
void Conductor::receiveOffer(const std::string &sdp) { | |||||
webrtc::SdpParseError error; | |||||
std::unique_ptr<webrtc::SessionDescriptionInterface> desc = | |||||
webrtc::CreateSessionDescription(webrtc::SdpType::kOffer, sdp, &error); | |||||
if (!desc.get()) { | |||||
observer_.onError(ConductorObserver::ErrorType::RECEIVE_OFFER, | |||||
std::move(error.description)); | |||||
return; | |||||
} | |||||
receiveOffer(std::move(desc)); | |||||
} | |||||
void Conductor::receiveOffer(std::unique_ptr<webrtc::SessionDescriptionInterface> offer) { | |||||
ensurePeerConnection(); | |||||
peer_conn_->SetRemoteDescription( | |||||
SetSessionDescriptionObserver::Create(observer_), | |||||
offer.release()); | |||||
peer_conn_->CreateAnswer( | |||||
this, webrtc::PeerConnectionInterface::RTCOfferAnswerOptions()); | |||||
} | |||||
void Conductor::receiveIceCandidate(std::string mid, int index, std::string sdp) { | |||||
webrtc::SdpParseError error; | |||||
std::unique_ptr<webrtc::IceCandidateInterface> candidate( | |||||
webrtc::CreateIceCandidate(mid, index, sdp, &error)); | |||||
if (!candidate.get()) { | |||||
observer_.onError(ConductorObserver::ErrorType::WARNING, | |||||
std::move(error.description)); | |||||
return; | |||||
} | |||||
if (!peer_conn_->AddIceCandidate(candidate.get())) { | |||||
observer_.onError(ConductorObserver::ErrorType::WARNING, | |||||
"Failed to apply the received candidate"); | |||||
return; | |||||
} | |||||
} | |||||
void Conductor::ensurePeerConnection() { | |||||
if (peer_conn_factory_.get()) | |||||
return; | |||||
peer_conn_factory_ = doCreatePeerConnectionFactory(); | |||||
peer_conn_ = doCreatePeerConnection(*peer_conn_factory_); | |||||
} | |||||
void Conductor::OnAddTrack( | |||||
rtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver, | |||||
const std::vector<rtc::scoped_refptr<webrtc::MediaStreamInterface>> &streams) { | |||||
observer_.onRemoteTrack(std::move(receiver->track())); | |||||
} | |||||
void Conductor::OnRemoveTrack( | |||||
rtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver) { | |||||
observer_.removeRemoteTrack(std::move(receiver->track())); | |||||
} | |||||
void Conductor::OnIceCandidate(const webrtc::IceCandidateInterface* candidate) { | |||||
std::string sdp; | |||||
if (!candidate->ToString(&sdp)) { | |||||
observer_.onError(ConductorObserver::ErrorType::WARNING, | |||||
"Failed to serialize candidate"); | |||||
return; | |||||
} | |||||
observer_.onIceCandidate( | |||||
std::move(candidate->sdp_mid()), | |||||
candidate->sdp_mline_index(), | |||||
std::move(sdp)); | |||||
} | |||||
rtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> | |||||
Conductor::doCreatePeerConnectionFactory() { | |||||
return webrtc::CreatePeerConnectionFactory( | |||||
nullptr /* network_thread */, nullptr /* worker_thread */, | |||||
nullptr /* signaling_thread */, nullptr /* default_adm */, | |||||
webrtc::CreateBuiltinAudioEncoderFactory(), | |||||
webrtc::CreateBuiltinAudioDecoderFactory(), | |||||
webrtc::CreateBuiltinVideoEncoderFactory(), | |||||
webrtc::CreateBuiltinVideoDecoderFactory(), nullptr /* audio_mixer */, | |||||
nullptr /* audio_processing */); | |||||
} | |||||
rtc::scoped_refptr<webrtc::PeerConnectionInterface> | |||||
Conductor::doCreatePeerConnection(webrtc::PeerConnectionFactoryInterface &factory) { | |||||
webrtc::PeerConnectionInterface::RTCConfiguration config; | |||||
config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan; | |||||
webrtc::PeerConnectionInterface::IceServer server; | |||||
const char *uri = getenv("WEBRTC_CONNECT"); | |||||
if (uri) { | |||||
server.uri = uri; | |||||
} else { | |||||
server.uri = "stun:stun.l.google.com:19302"; | |||||
} | |||||
config.servers.push_back(server); | |||||
rtc::scoped_refptr<webrtc::PeerConnectionInterface> conn = | |||||
factory.CreatePeerConnection( | |||||
config, nullptr, nullptr, this); | |||||
doAddTracks(*conn); | |||||
return conn; | |||||
} | |||||
void Conductor::OnSuccess(webrtc::SessionDescriptionInterface *desc) { | |||||
peer_conn_->SetLocalDescription( | |||||
SetSessionDescriptionObserver::Create(observer_), desc); | |||||
std::string sdp; | |||||
desc->ToString(&sdp); | |||||
observer_.onAnswer(std::move(sdp)); | |||||
} | |||||
void Conductor::OnFailure(webrtc::RTCError error) { | |||||
observer_.onError(ConductorObserver::ErrorType::WARNING, | |||||
error.message()); | |||||
} | |||||
} |