Chromium Code Reviews| Index: webrtc/ortc/ortcfactory.cc |
| diff --git a/webrtc/ortc/ortcfactory.cc b/webrtc/ortc/ortcfactory.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..0b7903b53eb786a05fcaba9a3d7b839cb1ca4b32 |
| --- /dev/null |
| +++ b/webrtc/ortc/ortcfactory.cc |
| @@ -0,0 +1,489 @@ |
| +/* |
| + * Copyright 2017 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 "webrtc/ortc/ortcfactory.h" |
| + |
| +#include <vector> |
| +#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/ortc/rtpreceivershim.h" |
| +#include "webrtc/ortc/rtpsendershim.h" |
| +#include "webrtc/ortc/rtptransportcontrollershim.h" |
| +#include "webrtc/ortc/rtptransportshim.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/videocapturertracksource.h" |
| +#include "webrtc/pc/videotrack.h" |
| + |
| +namespace { |
| + |
| +void ConvertFeedbackParam(const cricket::FeedbackParam& cricket_feedback, |
| + webrtc::RtpCodecCapability* codec) { |
|
pthatcher1
2017/02/10 22:36:52
Would a better name be AddCodecCapabilityFeedbackP
Taylor Brandstetter
2017/02/14 06:55:05
As long as we're renaming it, AddRtcpFeedbackFromF
pthatcher1
2017/02/17 23:10:22
I like it
|
| + 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") { |
|
pthatcher1
2017/02/10 22:36:52
Why not use kRtcpFbParamCcm, kRtcpFbCcmParamFir, a
Taylor Brandstetter
2017/02/14 06:55:04
Done.
|
| + if (cricket_feedback.param() == "fir") { |
| + codec->rtcp_feedback.emplace_back(webrtc::RtcpFeedbackType::CCM, |
| + webrtc::RtcpFeedbackMessageType::FIR); |
| + } else { |
| + RTC_NOTREACHED(); |
|
pthatcher1
2017/02/10 22:36:52
Wouldn't an RTC_DCHECK be more reasonable?
Here
Taylor Brandstetter
2017/02/14 06:55:05
How would that work?
pthatcher1
2017/02/17 23:10:22
I went through and read other uses of RTC_NOTREACH
|
| + } |
| + } 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); |
| +} |
|
pthatcher1
2017/02/10 22:36:52
Would FromCricketCodec be a better name? Or ToCod
Taylor Brandstetter
2017/02/14 06:55:04
This is only converting the audio or video specifi
|
| + |
| +template <class C> |
| +webrtc::RtpCapabilities ConvertCapabilities( |
| + cricket::MediaType kind, |
| + const std::vector<C> cricket_codecs, |
| + const cricket::RtpHeaderExtensions& cricket_extensions) { |
|
pthatcher1
2017/02/10 22:36:52
Would ToRtpCapabilites or FromCricketCapabilities
Taylor Brandstetter
2017/02/14 06:55:05
Done.
|
| + 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") { |
|
pthatcher1
2017/02/10 22:36:52
Why not use kRedCodecName, kUlpfecCodecName, and o
Taylor Brandstetter
2017/02/14 06:55:04
Done.
|
| + 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_METHOD2(RTCErrorOr<std::unique_ptr<OrtcRtpSenderInterface>>, |
| + CreateRtpSender, |
| + rtc::scoped_refptr<MediaStreamTrackInterface>, |
| + RtpTransportInterface*) |
| +PROXY_METHOD2(RTCErrorOr<std::unique_ptr<OrtcRtpSenderInterface>>, |
| + CreateRtpSender, |
| + cricket::MediaType, |
| + RtpTransportInterface*) |
| +PROXY_CONSTMETHOD1(RtpCapabilities, |
| + GetRtpReceiverCapabilities, |
| + cricket::MediaType) |
| +PROXY_METHOD2(RTCErrorOr<std::unique_ptr<OrtcRtpReceiverInterface>>, |
| + CreateRtpReceiver, |
| + cricket::MediaType, |
| + 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 |
| +RTCErrorOr<std::unique_ptr<OrtcFactoryInterface>> OrtcFactoryInterface::Create( |
| + rtc::Thread* network_thread, |
| + rtc::Thread* signaling_thread, |
| + rtc::NetworkManager* network_manager, |
| + rtc::PacketSocketFactory* socket_factory, |
| + AudioDeviceModule* adm) { |
| + // Hop to signaling thread if needed. |
| + if (signaling_thread && !signaling_thread->IsCurrent()) { |
| + 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.release()); |
| +} |
| + |
| +OrtcFactory::OrtcFactory(rtc::Thread* network_thread, |
| + rtc::Thread* signaling_thread, |
| + rtc::NetworkManager* network_manager, |
| + rtc::PacketSocketFactory* socket_factory, |
| + AudioDeviceModule* adm) |
| + : network_thread_(network_thread), |
| + signaling_thread_(signaling_thread), |
| + network_manager_(network_manager), |
| + 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(); |
| + network_thread_ = owned_network_thread_.get(); |
| + } |
| + |
| + // 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. |
| + worker_thread_ = rtc::Thread::Create(); |
| + worker_thread_->Start(); |
| + |
| + if (signaling_thread_) { |
| + RTC_DCHECK_RUN_ON(signaling_thread_); |
| + } else { |
| + signaling_thread_ = rtc::Thread::Current(); |
| + if (!signaling_thread_) { |
| + // If this thread isn't already wrapped by an rtc::Thread, create a |
| + // wrapper and own it in this class. |
| + signaling_thread_ = rtc::ThreadManager::Instance()->WrapCurrentThread(); |
| + wraps_signaling_thread_ = true; |
| + } |
| + } |
| + if (!network_manager_) { |
| + owned_network_manager_.reset(new rtc::BasicNetworkManager()); |
| + network_manager_ = owned_network_manager_.get(); |
| + } |
| + if (!socket_factory_) { |
| + owned_socket_factory_.reset( |
| + new rtc::BasicPacketSocketFactory(network_thread_)); |
| + socket_factory_ = owned_socket_factory_.get(); |
| + } |
| +} |
| + |
| +OrtcFactory::~OrtcFactory() { |
| + RTC_DCHECK_RUN_ON(signaling_thread_); |
| + if (wraps_signaling_thread_) { |
| + rtc::ThreadManager::Instance()->UnwrapCurrentThread(); |
| + } |
| +} |
| + |
| +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) { |
| + if (transport_controller) { |
| + return RtpTransportShim::CreateProxied(rtcp_parameters, rtp, rtcp, |
| + transport_controller); |
| + } else { |
| + // If |transport_controller| is null, create one automatically, which the |
| + // returned RtpTransport will own. |
| + auto result = CreateRtpTransportController(); |
| + if (!result.ok()) { |
| + return result.error(); |
| + } |
| + return RtpTransportShim::CreateProxied(rtcp_parameters, rtp, rtcp, |
| + result.ConsumeValue()); |
| + } |
| +} |
| + |
| +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, |
| + RtpTransportInterface* transport) { |
| + if (!track) { |
| + return CreateAndLogError(RTCErrorType::INVALID_PARAMETER, |
| + "Cannot pass null track into CreateRtpSender."); |
| + } |
| + auto result = |
| + CreateRtpSender(cricket::MediaTypeFromString(track->kind()), 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, |
| + 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, 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, |
| + 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, 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) { |
| + // 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 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 std::unique_ptr<cricket::MediaEngineInterface>( |
| + cricket::WebRtcMediaEngineFactory::Create(adm_, audio_decoder_factory_, |
| + nullptr, nullptr, nullptr)); |
| +} |
| + |
| +} // namespace webrtc |