Index: webrtc/pc/ortcfactory.cc |
diff --git a/webrtc/pc/ortcfactory.cc b/webrtc/pc/ortcfactory.cc |
index 47d39b7cb0e7b8a5bf4a8b56048eb25154fbbc9d..301086ec48ccfee189b3b1bd98698773046cd0cb 100644 |
--- a/webrtc/pc/ortcfactory.cc |
+++ b/webrtc/pc/ortcfactory.cc |
@@ -13,43 +13,218 @@ |
#include <string> |
#include <utility> // For std::move. |
+#include "webrtc/api/proxy.h" |
+#include "webrtc/api/mediastreamtrackproxy.h" |
+#include "webrtc/api/videosourceproxy.h" |
#include "webrtc/base/bind.h" |
#include "webrtc/base/asyncpacketsocket.h" |
+#include "webrtc/base/logging.h" |
+#include "webrtc/logging/rtc_event_log/rtc_event_log.h" |
+#include "webrtc/modules/audio_coding/codecs/builtin_audio_decoder_factory.h" |
#include "webrtc/p2p/base/basicpacketsocketfactory.h" |
#include "webrtc/p2p/base/udptransport.h" |
+#include "webrtc/pc/channelmanager.h" |
+#include "webrtc/pc/localaudiosource.h" |
+#include "webrtc/pc/audiotrack.h" |
+#include "webrtc/pc/rtpreceivershim.h" |
+#include "webrtc/pc/rtpsendershim.h" |
+#include "webrtc/pc/rtptransportcontrollershim.h" |
+#include "webrtc/pc/rtptransportshim.h" |
+#include "webrtc/pc/videocapturertracksource.h" |
+#include "webrtc/pc/videotrack.h" |
+ |
+namespace { |
+ |
+void ConvertFeedbackParam(const cricket::FeedbackParam& cricket_feedback, |
+ webrtc::RtpCodecCapability* codec) { |
+ if (cricket_feedback.id() == "ack") { |
+ RTC_DCHECK(cricket_feedback.param().empty()); |
+ codec->rtcp_feedback.emplace_back(webrtc::RtcpFeedbackType::ACK); |
+ } else if (cricket_feedback.id() == "ccm") { |
+ if (cricket_feedback.param() == "fir") { |
+ codec->rtcp_feedback.emplace_back(webrtc::RtcpFeedbackType::CCM, |
+ webrtc::RtcpFeedbackMessageType::FIR); |
+ } else { |
+ RTC_NOTREACHED(); |
+ } |
+ } else if (cricket_feedback.id() == "nack") { |
+ if (cricket_feedback.param().empty()) { |
+ codec->rtcp_feedback.emplace_back( |
+ webrtc::RtcpFeedbackType::NACK, |
+ webrtc::RtcpFeedbackMessageType::GENERIC_NACK); |
+ } else if (cricket_feedback.param() == "pli") { |
+ codec->rtcp_feedback.emplace_back(webrtc::RtcpFeedbackType::NACK, |
+ webrtc::RtcpFeedbackMessageType::PLI); |
+ } else { |
+ RTC_NOTREACHED(); |
+ } |
+ } else if (cricket_feedback.id() == "goog-remb") { |
+ RTC_DCHECK(cricket_feedback.param().empty()); |
+ codec->rtcp_feedback.emplace_back(webrtc::RtcpFeedbackType::REMB); |
+ } else if (cricket_feedback.id() == "transport-cc") { |
+ RTC_DCHECK(cricket_feedback.param().empty()); |
+ codec->rtcp_feedback.emplace_back(webrtc::RtcpFeedbackType::TRANSPORT_CC); |
+ } else { |
+ RTC_NOTREACHED(); |
+ } |
+} |
+ |
+template <typename C> |
+void CodecSpecificConversion(const C& cricket_codec, |
+ webrtc::RtpCodecCapability* codec) {} |
+ |
+template <> |
+void CodecSpecificConversion<cricket::AudioCodec>( |
+ const cricket::AudioCodec& cricket_codec, |
+ webrtc::RtpCodecCapability* codec) { |
+ codec->num_channels = rtc::Optional<int>(cricket_codec.channels); |
+} |
+ |
+template <class C> |
+webrtc::RtpCapabilities ConvertCapabilities( |
+ cricket::MediaType kind, |
+ const std::vector<C> cricket_codecs, |
+ const cricket::RtpHeaderExtensions& cricket_extensions) { |
+ webrtc::RtpCapabilities capabilities; |
+ bool have_red = false; |
+ bool have_ulpfec = false; |
+ bool have_flexfec = false; |
+ for (const C& cricket_codec : cricket_codecs) { |
+ if (cricket_codec.name == "red") { |
+ have_red = true; |
+ } else if (cricket_codec.name == "ulpfec") { |
+ have_ulpfec = true; |
+ } else if (cricket_codec.name == "flexfec") { |
+ have_flexfec = true; |
+ } |
+ webrtc::RtpCodecCapability codec; |
+ codec.name = cricket_codec.name; |
+ codec.kind = kind; |
+ codec.clock_rate = rtc::Optional<int>(cricket_codec.clockrate); |
+ codec.preferred_payload_type = rtc::Optional<int>(cricket_codec.id); |
+ for (const cricket::FeedbackParam& cricket_feedback : |
+ cricket_codec.feedback_params.params()) { |
+ ConvertFeedbackParam(cricket_feedback, &codec); |
+ } |
+ CodecSpecificConversion(cricket_codec, &codec); |
+ codec.parameters.insert(cricket_codec.params.begin(), |
+ cricket_codec.params.end()); |
+ capabilities.codecs.push_back(std::move(codec)); |
+ } |
+ for (const webrtc::RtpExtension& cricket_extension : cricket_extensions) { |
+ capabilities.header_extensions.emplace_back(cricket_extension.uri, |
+ cricket_extension.id); |
+ } |
+ if (have_red) { |
+ capabilities.fec.push_back(webrtc::FecMechanism::RED); |
+ } |
+ if (have_red && have_ulpfec) { |
+ capabilities.fec.push_back(webrtc::FecMechanism::RED_AND_ULPFEC); |
+ } |
+ if (have_flexfec) { |
+ capabilities.fec.push_back(webrtc::FecMechanism::FLEXFEC); |
+ } |
+ return capabilities; |
+} |
+ |
+} // namespace |
namespace webrtc { |
+// Note that this proxy class uses the network thread as the "worker" thread. |
+BEGIN_OWNED_PROXY_MAP(OrtcFactory) |
+PROXY_SIGNALING_THREAD_DESTRUCTOR() |
+PROXY_METHOD0(RTCErrorOr<std::unique_ptr<RtpTransportControllerInterface>>, |
+ CreateRtpTransportController) |
+PROXY_METHOD4(RTCErrorOr<std::unique_ptr<RtpTransportInterface>>, |
+ CreateRtpTransport, |
+ const RtcpParameters&, |
+ PacketTransportInterface*, |
+ PacketTransportInterface*, |
+ RtpTransportControllerInterface*) |
+PROXY_CONSTMETHOD1(RtpCapabilities, |
+ GetRtpSenderCapabilities, |
+ cricket::MediaType) |
+PROXY_METHOD3(RTCErrorOr<std::unique_ptr<OrtcRtpSenderInterface>>, |
+ CreateRtpSender, |
+ rtc::scoped_refptr<MediaStreamTrackInterface>, |
+ const RtpParameters&, |
+ RtpTransportInterface*) |
+PROXY_METHOD3(RTCErrorOr<std::unique_ptr<OrtcRtpSenderInterface>>, |
+ CreateRtpSender, |
+ cricket::MediaType, |
+ const RtpParameters&, |
+ RtpTransportInterface*) |
+PROXY_CONSTMETHOD1(RtpCapabilities, |
+ GetRtpReceiverCapabilities, |
+ cricket::MediaType) |
+PROXY_METHOD3(RTCErrorOr<std::unique_ptr<OrtcRtpReceiverInterface>>, |
+ CreateRtpReceiver, |
+ cricket::MediaType, |
+ const RtpParameters&, |
+ RtpTransportInterface*) |
+PROXY_WORKER_METHOD3(RTCErrorOr<std::unique_ptr<UdpTransportInterface>>, |
+ CreateUdpTransport, |
+ int, |
+ uint16_t, |
+ uint16_t) |
+PROXY_METHOD1(rtc::scoped_refptr<AudioSourceInterface>, |
+ CreateAudioSource, |
+ const cricket::AudioOptions&) |
+PROXY_METHOD2(rtc::scoped_refptr<VideoTrackSourceInterface>, |
+ CreateVideoSource, |
+ std::unique_ptr<cricket::VideoCapturer>, |
+ const MediaConstraintsInterface*) |
+PROXY_METHOD2(rtc::scoped_refptr<VideoTrackInterface>, |
+ CreateVideoTrack, |
+ const std::string&, |
+ VideoTrackSourceInterface*) |
+PROXY_METHOD2(rtc::scoped_refptr<AudioTrackInterface>, |
+ CreateAudioTrack, |
+ const std::string&, |
+ AudioSourceInterface*) |
+END_PROXY_MAP() |
+ |
// static |
-std::unique_ptr<OrtcFactoryInterface> OrtcFactoryInterface::Create( |
+RTCErrorOr<std::unique_ptr<OrtcFactoryInterface>> OrtcFactoryInterface::Create( |
rtc::Thread* network_thread, |
rtc::Thread* signaling_thread, |
rtc::NetworkManager* network_manager, |
- rtc::PacketSocketFactory* socket_factory) { |
+ rtc::PacketSocketFactory* socket_factory, |
+ AudioDeviceModule* adm) { |
// Hop to signaling thread if needed. |
if (signaling_thread && !signaling_thread->IsCurrent()) { |
- return signaling_thread->Invoke<std::unique_ptr<OrtcFactoryInterface>>( |
- RTC_FROM_HERE, |
- rtc::Bind(&OrtcFactoryInterface::Create, network_thread, |
- signaling_thread, network_manager, socket_factory)); |
- } |
- OrtcFactory* new_factory = |
- new OrtcFactory(network_thread, signaling_thread, |
- network_manager, socket_factory); |
+ return signaling_thread |
+ ->Invoke<RTCErrorOr<std::unique_ptr<OrtcFactoryInterface>>>( |
+ RTC_FROM_HERE, |
+ rtc::Bind(&OrtcFactoryInterface::Create, network_thread, |
+ signaling_thread, network_manager, socket_factory, adm)); |
+ } |
+ std::unique_ptr<OrtcFactory> new_factory(new OrtcFactory( |
+ network_thread, signaling_thread, network_manager, socket_factory, adm)); |
+ RTCError err = new_factory->Initialize(); |
+ if (!err.ok()) { |
+ return err; |
+ } |
// Return a proxy so that any calls on the returned object (including |
// destructor) happen on the signaling thread. |
return OrtcFactoryProxy::Create(new_factory->signaling_thread(), |
- new_factory->network_thread(), new_factory); |
+ new_factory->network_thread(), |
+ new_factory.release()); |
} |
OrtcFactory::OrtcFactory(rtc::Thread* network_thread, |
rtc::Thread* signaling_thread, |
rtc::NetworkManager* network_manager, |
- rtc::PacketSocketFactory* socket_factory) |
+ rtc::PacketSocketFactory* socket_factory, |
+ AudioDeviceModule* adm) |
: network_thread_(network_thread), |
signaling_thread_(signaling_thread), |
network_manager_(network_manager), |
- socket_factory_(socket_factory) { |
+ socket_factory_(socket_factory), |
+ adm_(adm), |
+ null_event_log_(RtcEventLog::CreateNull()), |
+ audio_decoder_factory_(CreateBuiltinAudioDecoderFactory()) { |
if (!network_thread_) { |
owned_network_thread_ = rtc::Thread::CreateWithSocketServer(); |
owned_network_thread_->Start(); |
@@ -58,8 +233,8 @@ OrtcFactory::OrtcFactory(rtc::Thread* network_thread, |
// The worker thread is created internally because it's an implementation |
// detail, and consumers of the API don't need to really know about it. |
- owned_worker_thread_ = rtc::Thread::Create(); |
- owned_worker_thread_->Start(); |
+ worker_thread_ = rtc::Thread::Create(); |
+ worker_thread_->Start(); |
if (signaling_thread_) { |
RTC_DCHECK_RUN_ON(signaling_thread_); |
@@ -90,30 +265,221 @@ OrtcFactory::~OrtcFactory() { |
} |
} |
-std::unique_ptr<UdpTransportInterface> OrtcFactory::CreateUdpTransport( |
- int family, |
- uint16_t min_port, |
- uint16_t max_port) { |
- if (!network_thread_->IsCurrent()) { |
- RTC_DCHECK_RUN_ON(signaling_thread_); |
- return network_thread_->Invoke<std::unique_ptr<UdpTransportInterface>>( |
- RTC_FROM_HERE, rtc::Bind(&OrtcFactory::CreateUdpTransport, this, family, |
- min_port, max_port)); |
+RTCErrorOr<std::unique_ptr<RtpTransportControllerInterface>> |
+OrtcFactory::CreateRtpTransportController() { |
+ return RtpTransportControllerShim::CreateProxied( |
+ cricket::MediaConfig(), channel_manager_.get(), null_event_log_.get(), |
+ signaling_thread_, worker_thread_.get()); |
+} |
+ |
+RTCErrorOr<std::unique_ptr<RtpTransportInterface>> |
+OrtcFactory::CreateRtpTransport( |
+ const RtcpParameters& rtcp_parameters, |
+ PacketTransportInterface* rtp, |
+ PacketTransportInterface* rtcp, |
+ RtpTransportControllerInterface* transport_controller) { |
+ return RtpTransportShim::CreateProxied(rtcp_parameters, rtp, rtcp, |
+ transport_controller); |
+} |
+ |
+RtpCapabilities OrtcFactory::GetRtpSenderCapabilities( |
+ cricket::MediaType kind) const { |
+ switch (kind) { |
+ case cricket::MEDIA_TYPE_AUDIO: { |
+ cricket::AudioCodecs cricket_codecs; |
+ cricket::RtpHeaderExtensions cricket_extensions; |
+ channel_manager_->GetSupportedAudioSendCodecs(&cricket_codecs); |
+ channel_manager_->GetSupportedAudioRtpHeaderExtensions( |
+ &cricket_extensions); |
+ return ConvertCapabilities(kind, cricket_codecs, cricket_extensions); |
+ } |
+ case cricket::MEDIA_TYPE_VIDEO: { |
+ cricket::VideoCodecs cricket_codecs; |
+ cricket::RtpHeaderExtensions cricket_extensions; |
+ channel_manager_->GetSupportedVideoCodecs(&cricket_codecs); |
+ channel_manager_->GetSupportedVideoRtpHeaderExtensions( |
+ &cricket_extensions); |
+ return ConvertCapabilities(kind, cricket_codecs, cricket_extensions); |
+ } |
+ case cricket::MEDIA_TYPE_DATA: |
+ return RtpCapabilities(); |
+ } |
+} |
+ |
+RTCErrorOr<std::unique_ptr<OrtcRtpSenderInterface>> |
+OrtcFactory::CreateRtpSender( |
+ rtc::scoped_refptr<MediaStreamTrackInterface> track, |
+ const RtpParameters& rtp_parameters, |
+ RtpTransportInterface* transport) { |
+ if (!track) { |
+ return CreateAndLogError(RTCErrorType::INVALID_PARAMETER, |
+ "Cannot pass null track into CreateRtpSender."); |
+ } |
+ auto result = CreateRtpSender(cricket::MediaTypeFromString(track->kind()), |
+ rtp_parameters, transport); |
+ if (!result.ok()) { |
+ return result; |
+ } |
+ auto err = result.value()->SetTrack(track); |
+ if (!err.ok()) { |
+ return err; |
} |
+ return result; |
+} |
+ |
+RTCErrorOr<std::unique_ptr<OrtcRtpSenderInterface>> |
+OrtcFactory::CreateRtpSender(cricket::MediaType kind, |
+ const RtpParameters& rtp_parameters, |
+ RtpTransportInterface* transport) { |
+ if (kind == cricket::MEDIA_TYPE_DATA) { |
+ return CreateAndLogError(RTCErrorType::INVALID_PARAMETER, |
+ "Cannot create data RtpSender."); |
+ } |
+ if (!transport) { |
+ return CreateAndLogError( |
+ RTCErrorType::INVALID_PARAMETER, |
+ "Cannot pass null transport into CreateRtpSender."); |
+ } |
+ return RtpSenderShim::CreateProxied(kind, rtp_parameters, |
+ transport->GetInternal()); |
+} |
+ |
+RtpCapabilities OrtcFactory::GetRtpReceiverCapabilities( |
+ cricket::MediaType kind) const { |
+ switch (kind) { |
+ case cricket::MEDIA_TYPE_AUDIO: { |
+ cricket::AudioCodecs cricket_codecs; |
+ cricket::RtpHeaderExtensions cricket_extensions; |
+ channel_manager_->GetSupportedAudioReceiveCodecs(&cricket_codecs); |
+ channel_manager_->GetSupportedAudioRtpHeaderExtensions( |
+ &cricket_extensions); |
+ return ConvertCapabilities(kind, cricket_codecs, cricket_extensions); |
+ } |
+ case cricket::MEDIA_TYPE_VIDEO: { |
+ cricket::VideoCodecs cricket_codecs; |
+ cricket::RtpHeaderExtensions cricket_extensions; |
+ channel_manager_->GetSupportedVideoCodecs(&cricket_codecs); |
+ channel_manager_->GetSupportedVideoRtpHeaderExtensions( |
+ &cricket_extensions); |
+ return ConvertCapabilities(kind, cricket_codecs, cricket_extensions); |
+ } |
+ case cricket::MEDIA_TYPE_DATA: |
+ return RtpCapabilities(); |
+ } |
+} |
+ |
+RTCErrorOr<std::unique_ptr<OrtcRtpReceiverInterface>> |
+OrtcFactory::CreateRtpReceiver(cricket::MediaType kind, |
+ const RtpParameters& rtp_parameters, |
+ RtpTransportInterface* transport) { |
+ if (kind == cricket::MEDIA_TYPE_DATA) { |
+ return CreateAndLogError(RTCErrorType::INVALID_PARAMETER, |
+ "Cannot create data RtpReceiver."); |
+ } |
+ if (!transport) { |
+ return CreateAndLogError( |
+ RTCErrorType::INVALID_PARAMETER, |
+ "Cannot pass null transport into CreateRtpReceiver."); |
+ } |
+ return RtpReceiverShim::CreateProxied(kind, rtp_parameters, |
+ transport->GetInternal()); |
+} |
+ |
+BEGIN_OWNED_PROXY_MAP(UdpTransport) |
+PROXY_WORKER_THREAD_DESTRUCTOR() |
+PROXY_WORKER_CONSTMETHOD0(rtc::SocketAddress, GetLocalAddress) |
+PROXY_WORKER_METHOD1(bool, SetRemoteAddress, const rtc::SocketAddress&) |
+PROXY_WORKER_CONSTMETHOD0(rtc::SocketAddress, GetRemoteAddress) |
+protected: |
+rtc::PacketTransportInternal* GetInternal() override { |
+ return internal(); |
+} |
+END_PROXY_MAP() |
+ |
+RTCErrorOr<std::unique_ptr<UdpTransportInterface>> |
+OrtcFactory::CreateUdpTransport(int family, |
+ uint16_t min_port, |
+ uint16_t max_port) { |
+ RTC_DCHECK_RUN_ON(network_thread_); |
std::unique_ptr<rtc::AsyncPacketSocket> socket( |
socket_factory_->CreateUdpSocket( |
rtc::SocketAddress(rtc::GetAnyIP(family), 0), min_port, max_port)); |
if (!socket) { |
- LOG(LS_WARNING) << "Local socket allocation failure."; |
- return nullptr; |
+ // Only log at warning level, because this method may be called with |
+ // specific port ranges to determine if a port is available, expecting the |
+ // possibility of an error. |
+ return CreateAndLogError(RTCErrorType::RESOURCE_EXHAUSTED, |
+ "Local socket allocation failure.", |
+ rtc::LS_WARNING); |
} |
LOG(LS_INFO) << "Created UDP socket with address " |
<< socket->GetLocalAddress().ToSensitiveString() << "."; |
// Use proxy so that calls to the returned object are invoked on the network |
// thread. |
- return UdpTransportProxy::Create( |
+ return UdpTransportProxyWithInternal<cricket::UdpTransport>::Create( |
signaling_thread_, network_thread_, |
new cricket::UdpTransport(std::string(), std::move(socket))); |
} |
+rtc::scoped_refptr<AudioSourceInterface> OrtcFactory::CreateAudioSource( |
+ const cricket::AudioOptions& options) { |
+ RTC_DCHECK_RUN_ON(signaling_thread_); |
+ return rtc::scoped_refptr<LocalAudioSource>( |
+ LocalAudioSource::Create(&options)); |
+} |
+ |
+rtc::scoped_refptr<VideoTrackSourceInterface> OrtcFactory::CreateVideoSource( |
+ std::unique_ptr<cricket::VideoCapturer> capturer, |
+ const MediaConstraintsInterface* constraints) { |
+ RTC_DCHECK_RUN_ON(signaling_thread_); |
+ rtc::scoped_refptr<VideoTrackSourceInterface> source( |
+ VideoCapturerTrackSource::Create( |
+ worker_thread_.get(), std::move(capturer), constraints, false)); |
+ return VideoTrackSourceProxy::Create(signaling_thread_, worker_thread_.get(), |
+ source); |
+} |
+ |
+rtc::scoped_refptr<VideoTrackInterface> OrtcFactory::CreateVideoTrack( |
+ const std::string& id, |
+ VideoTrackSourceInterface* source) { |
+ RTC_DCHECK_RUN_ON(signaling_thread_); |
+ rtc::scoped_refptr<VideoTrackInterface> track(VideoTrack::Create(id, source)); |
+ return VideoTrackProxy::Create(signaling_thread_, worker_thread_.get(), |
+ track); |
+} |
+ |
+rtc::scoped_refptr<AudioTrackInterface> OrtcFactory::CreateAudioTrack( |
+ const std::string& id, |
+ AudioSourceInterface* source) { |
+ RTC_DCHECK_RUN_ON(signaling_thread_); |
+ rtc::scoped_refptr<AudioTrackInterface> track(AudioTrack::Create(id, source)); |
+ return AudioTrackProxy::Create(signaling_thread_, track); |
+} |
+ |
+RTCError OrtcFactory::Initialize() { |
+ // TODO(deadbeef): Get rid of requirement to hop to worker thread here. |
+ std::unique_ptr<cricket::MediaEngineInterface> media_engine = |
+ worker_thread_->Invoke<std::unique_ptr<cricket::MediaEngineInterface>>( |
+ RTC_FROM_HERE, rtc::Bind(&OrtcFactory::CreateMediaEngine_w, this)); |
+ |
+ channel_manager_.reset(new cricket::ChannelManager( |
+ std::move(media_engine), worker_thread_.get(), network_thread_)); |
+ channel_manager_->SetVideoRtxEnabled(true); |
+ if (!channel_manager_->Init()) { |
+ return CreateAndLogError(RTCErrorType::INTERNAL_ERROR, |
+ "Failed to initialize ChannelManager."); |
+ } |
+ return RTCError(); |
+} |
+ |
+std::unique_ptr<cricket::MediaEngineInterface> |
+OrtcFactory::CreateMediaEngine_w() { |
+ RTC_DCHECK_RUN_ON(worker_thread_.get()); |
+ // The nullptr arguments are optional factories that could be passed into the |
+ // OrtcFactory, but aren't yet. |
+ // Note that |adm_| may be null. |
+ return cricket::WebRtcMediaEngineFactory::Create(adm_, audio_decoder_factory_, |
+ nullptr, nullptr, nullptr); |
+} |
+ |
} // namespace webrtc |