session.cc 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032
  1. // Copyright 2018 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "components/mirroring/service/session.h"
  5. #include <algorithm>
  6. #include <memory>
  7. #include <string>
  8. #include <utility>
  9. #include <vector>
  10. #include "base/bind.h"
  11. #include "base/cpu.h"
  12. #include "base/json/json_writer.h"
  13. #include "base/logging.h"
  14. #include "base/memory/ptr_util.h"
  15. #include "base/memory/raw_ptr.h"
  16. #include "base/metrics/histogram_macros.h"
  17. #include "base/no_destructor.h"
  18. #include "base/rand_util.h"
  19. #include "base/strings/strcat.h"
  20. #include "base/strings/string_number_conversions.h"
  21. #include "base/strings/string_util.h"
  22. #include "base/strings/stringprintf.h"
  23. #include "base/system/sys_info.h"
  24. #include "base/task/thread_pool.h"
  25. #include "base/threading/thread_task_runner_handle.h"
  26. #include "base/time/default_tick_clock.h"
  27. #include "base/time/time.h"
  28. #include "base/values.h"
  29. #include "build/build_config.h"
  30. #include "build/chromeos_buildflags.h"
  31. #include "components/mirroring/service/captured_audio_input.h"
  32. #include "components/mirroring/service/mirroring_features.h"
  33. #include "components/mirroring/service/udp_socket_client.h"
  34. #include "components/mirroring/service/video_capture_client.h"
  35. #include "crypto/random.h"
  36. #include "media/audio/audio_input_device.h"
  37. #include "media/base/audio_capturer_source.h"
  38. #include "media/base/bind_to_current_loop.h"
  39. #include "media/cast/encoding/external_video_encoder.h"
  40. #include "media/cast/net/cast_transport.h"
  41. #include "media/cast/sender/audio_sender.h"
  42. #include "media/cast/sender/video_sender.h"
  43. #include "media/gpu/gpu_video_accelerator_util.h"
  44. #include "media/mojo/clients/mojo_video_encode_accelerator.h"
  45. #include "media/video/video_encode_accelerator.h"
  46. #include "mojo/public/cpp/system/platform_handle.h"
  47. #include "net/base/ip_endpoint.h"
  48. #include "services/viz/public/cpp/gpu/gpu.h"
  49. #include "third_party/openscreen/src/cast/streaming/answer_messages.h"
  50. #include "third_party/openscreen/src/cast/streaming/capture_recommendations.h"
  51. #include "third_party/openscreen/src/cast/streaming/offer_messages.h"
  52. using media::cast::CastTransportStatus;
  53. using media::cast::Codec;
  54. using media::cast::FrameEvent;
  55. using media::cast::FrameSenderConfig;
  56. using media::cast::OperationalStatus;
  57. using media::cast::Packet;
  58. using media::cast::PacketEvent;
  59. using media::cast::RtpPayloadType;
  60. using media::mojom::RemotingSinkAudioCapability;
  61. using media::mojom::RemotingSinkVideoCapability;
  62. using mirroring::mojom::SessionError;
  63. using mirroring::mojom::SessionType;
  64. namespace mirroring {
  65. namespace {
  66. // The interval for CastTransport to send Frame/PacketEvents to Session for
  67. // logging.
  68. constexpr base::TimeDelta kSendEventsInterval = base::Seconds(1);
  69. // The duration for OFFER/ANSWER exchange. If timeout, notify the client that
  70. // the session failed to start.
  71. constexpr base::TimeDelta kOfferAnswerExchangeTimeout = base::Seconds(15);
  72. // Amount of time to wait before assuming the Cast Receiver does not support
  73. // querying for capabilities via GET_CAPABILITIES.
  74. constexpr base::TimeDelta kGetCapabilitiesTimeout = base::Seconds(30);
  75. // Used for OFFER/ANSWER message exchange. Some receivers will error out on
  76. // payloadType values other than the ones hard-coded here.
  77. constexpr int kAudioPayloadType = 127;
  78. constexpr int kVideoPayloadType = 96;
  79. constexpr int kAudioSsrcMin = 1;
  80. constexpr int kAudioSsrcMax = 5e5;
  81. constexpr int kVideoSsrcMin = 5e5 + 1;
  82. constexpr int kVideoSsrcMax = 10e5;
  83. // The implemented remoting version.
  84. constexpr int kSupportedRemotingVersion = 2;
  85. class TransportClient final : public media::cast::CastTransport::Client {
  86. public:
  87. explicit TransportClient(Session* session) : session_(session) {}
  88. TransportClient(const TransportClient&) = delete;
  89. TransportClient& operator=(const TransportClient&) = delete;
  90. ~TransportClient() override {}
  91. // media::cast::CastTransport::Client implementation.
  92. void OnStatusChanged(CastTransportStatus status) override {
  93. session_->OnTransportStatusChanged(status);
  94. }
  95. void OnLoggingEventsReceived(
  96. std::unique_ptr<std::vector<FrameEvent>> frame_events,
  97. std::unique_ptr<std::vector<PacketEvent>> packet_events) override {
  98. session_->OnLoggingEventsReceived(std::move(frame_events),
  99. std::move(packet_events));
  100. }
  101. void ProcessRtpPacket(std::unique_ptr<Packet> packet) override {
  102. NOTREACHED();
  103. }
  104. private:
  105. const raw_ptr<Session> session_; // Outlives this class.
  106. };
  107. // Generates a string with cryptographically secure random bytes.
  108. std::string MakeRandomString(size_t length) {
  109. std::string result(length, ' ');
  110. crypto::RandBytes(std::data(result), length);
  111. return result;
  112. }
  113. int NumberOfEncodeThreads() {
  114. // Do not saturate CPU utilization just for encoding. On a lower-end system
  115. // with only 1 or 2 cores, use only one thread for encoding. On systems with
  116. // more cores, allow half of the cores to be used for encoding.
  117. return std::min(8, (base::SysInfo::NumberOfProcessors() + 1) / 2);
  118. }
  119. // Helper to add |config| to |config_list| with given |aes_key|.
  120. void AddSenderConfig(int32_t sender_ssrc,
  121. FrameSenderConfig config,
  122. const std::string& aes_key,
  123. const std::string& aes_iv,
  124. const mojom::SessionParameters& session_params,
  125. std::vector<FrameSenderConfig>* config_list) {
  126. config.aes_key = aes_key;
  127. config.aes_iv_mask = aes_iv;
  128. config.sender_ssrc = sender_ssrc;
  129. if (session_params.target_playout_delay) {
  130. config.animated_playout_delay = session_params.target_playout_delay.value();
  131. config.min_playout_delay = session_params.target_playout_delay.value();
  132. config.max_playout_delay = session_params.target_playout_delay.value();
  133. }
  134. config_list->emplace_back(config);
  135. }
  136. // Generate the stream object from |config| and add it to |stream_list|.
  137. void AddStreamObject(int stream_index,
  138. const std::string& codec_name,
  139. const FrameSenderConfig& config,
  140. const MirrorSettings& mirror_settings,
  141. base::Value::List& stream_list) {
  142. base::Value::Dict stream;
  143. stream.Set("index", stream_index);
  144. stream.Set("codecName", base::ToLowerASCII(codec_name));
  145. stream.Set("rtpProfile", "cast");
  146. const bool is_audio =
  147. (config.rtp_payload_type <= media::cast::RtpPayloadType::AUDIO_LAST);
  148. stream.Set("rtpPayloadType",
  149. is_audio ? kAudioPayloadType : kVideoPayloadType);
  150. stream.Set("ssrc", static_cast<int>(config.sender_ssrc));
  151. stream.Set("targetDelay",
  152. static_cast<int>(config.animated_playout_delay.InMilliseconds()));
  153. stream.Set("aesKey",
  154. base::HexEncode(config.aes_key.data(), config.aes_key.size()));
  155. stream.Set("aesIvMask", base::HexEncode(config.aes_iv_mask.data(),
  156. config.aes_iv_mask.size()));
  157. stream.Set("timeBase", "1/" + std::to_string(config.rtp_timebase));
  158. stream.Set("receiverRtcpEventLog", true);
  159. stream.Set("rtpExtensions", "adaptive_playout_delay");
  160. if (is_audio) {
  161. // Note on "AUTO" bitrate calculation: This is based on libopus source
  162. // at the time of this writing. Internally, it uses the following math:
  163. //
  164. // packet_overhead_bps = 60 bits * num_packets_in_one_second
  165. // approx_encoded_signal_bps = frequency * channels
  166. // estimated_bps = packet_overhead_bps + approx_encoded_signal_bps
  167. //
  168. // For 100 packets/sec at 48 kHz and 2 channels, this is 102kbps.
  169. const int bitrate = config.max_bitrate > 0
  170. ? config.max_bitrate
  171. : (60 * config.max_frame_rate +
  172. config.rtp_timebase * config.channels);
  173. stream.Set("type", "audio_source");
  174. stream.Set("bitRate", bitrate);
  175. stream.Set("sampleRate", config.rtp_timebase);
  176. stream.Set("channels", config.channels);
  177. } else /* is video */ {
  178. stream.Set("type", "video_source");
  179. stream.Set("renderMode", "video");
  180. stream.Set("maxFrameRate",
  181. std::to_string(static_cast<int>(config.max_frame_rate * 1000)) +
  182. "/1000");
  183. stream.Set("maxBitRate", config.max_bitrate);
  184. base::Value::List resolutions;
  185. base::Value::Dict resolution;
  186. resolution.Set("width", mirror_settings.max_width());
  187. resolution.Set("height", mirror_settings.max_height());
  188. resolutions.Append(std::move(resolution));
  189. stream.Set("resolutions", std::move(resolutions));
  190. }
  191. stream_list.Append(std::move(stream));
  192. }
  193. // Convert the sink capabilities to media::mojom::RemotingSinkMetadata.
  194. media::mojom::RemotingSinkMetadata ToRemotingSinkMetadata(
  195. const std::vector<std::string>& capabilities,
  196. const std::string& receiver_name,
  197. const mojom::SessionParameters& params,
  198. const std::string& receiver_build_version) {
  199. media::mojom::RemotingSinkMetadata sink_metadata;
  200. sink_metadata.friendly_name = receiver_name;
  201. for (const auto& capability : capabilities) {
  202. if (capability == "audio") {
  203. sink_metadata.audio_capabilities.push_back(
  204. RemotingSinkAudioCapability::CODEC_BASELINE_SET);
  205. } else if (capability == "aac") {
  206. sink_metadata.audio_capabilities.push_back(
  207. RemotingSinkAudioCapability::CODEC_AAC);
  208. } else if (capability == "opus") {
  209. sink_metadata.audio_capabilities.push_back(
  210. RemotingSinkAudioCapability::CODEC_OPUS);
  211. } else if (capability == "video") {
  212. sink_metadata.video_capabilities.push_back(
  213. RemotingSinkVideoCapability::CODEC_BASELINE_SET);
  214. } else if (capability == "4k") {
  215. sink_metadata.video_capabilities.push_back(
  216. RemotingSinkVideoCapability::SUPPORT_4K);
  217. } else if (capability == "h264") {
  218. sink_metadata.video_capabilities.push_back(
  219. RemotingSinkVideoCapability::CODEC_H264);
  220. } else if (capability == "vp8") {
  221. sink_metadata.video_capabilities.push_back(
  222. RemotingSinkVideoCapability::CODEC_VP8);
  223. } else if (capability == "vp9") {
  224. // TODO(crbug.com/1198616): receiver_model_name hacks should be removed.
  225. if (base::StartsWith(params.receiver_model_name, "Chromecast Ultra",
  226. base::CompareCase::SENSITIVE)) {
  227. sink_metadata.video_capabilities.push_back(
  228. RemotingSinkVideoCapability::CODEC_VP9);
  229. }
  230. } else if (capability == "hevc") {
  231. // TODO(crbug.com/1198616): receiver_model_name hacks should be removed.
  232. if (base::StartsWith(params.receiver_model_name, "Chromecast Ultra",
  233. base::CompareCase::SENSITIVE)) {
  234. sink_metadata.video_capabilities.push_back(
  235. RemotingSinkVideoCapability::CODEC_HEVC);
  236. }
  237. } else {
  238. DVLOG(1) << "Unknown mediaCap name: " << capability;
  239. }
  240. }
  241. // Enable remoting 1080p 30fps or higher resolution/fps content for Chromecast
  242. // Ultra receivers only.
  243. // TODO(crbug.com/1198616): receiver_model_name hacks should be removed.
  244. if (params.receiver_model_name == "Chromecast Ultra") {
  245. sink_metadata.video_capabilities.push_back(
  246. RemotingSinkVideoCapability::SUPPORT_4K);
  247. }
  248. return sink_metadata;
  249. }
  250. bool ShouldQueryForRemotingCapabilities(
  251. const std::string& receiver_model_name) {
  252. if (base::FeatureList::IsEnabled(features::kCastForceEnableRemotingQuery)) {
  253. return true;
  254. }
  255. if (base::FeatureList::IsEnabled(
  256. features::kCastUseBlocklistForRemotingQuery)) {
  257. // The blocklist has not yet been fully determined.
  258. // TODO(b/224993260): Implement this blocklist.
  259. NOTREACHED();
  260. return false;
  261. }
  262. // This is a workaround for Nest Hub devices, which do not support remoting.
  263. // TODO(crbug.com/1198616): filtering hack should be removed. See
  264. // issuetracker.google.com/135725157 for more information.
  265. return base::StartsWith(receiver_model_name, "Chromecast",
  266. base::CompareCase::SENSITIVE) ||
  267. base::StartsWith(receiver_model_name, "Eureka Dongle",
  268. base::CompareCase::SENSITIVE);
  269. }
  270. } // namespace
  271. class Session::AudioCapturingCallback final
  272. : public media::AudioCapturerSource::CaptureCallback {
  273. public:
  274. using AudioDataCallback =
  275. base::RepeatingCallback<void(std::unique_ptr<media::AudioBus> audio_bus,
  276. const base::TimeTicks& recorded_time)>;
  277. AudioCapturingCallback(AudioDataCallback audio_data_callback,
  278. base::OnceClosure error_callback)
  279. : audio_data_callback_(std::move(audio_data_callback)),
  280. error_callback_(std::move(error_callback)) {
  281. DCHECK(!audio_data_callback_.is_null());
  282. }
  283. AudioCapturingCallback(const AudioCapturingCallback&) = delete;
  284. AudioCapturingCallback& operator=(const AudioCapturingCallback&) = delete;
  285. ~AudioCapturingCallback() override {}
  286. private:
  287. // media::AudioCapturerSource::CaptureCallback implementation.
  288. void OnCaptureStarted() override {}
  289. // Called on audio thread.
  290. void Capture(const media::AudioBus* audio_bus,
  291. base::TimeTicks audio_capture_time,
  292. double volume,
  293. bool key_pressed) override {
  294. // TODO(crbug.com/1015467): Don't copy the audio data. Instead, send
  295. // |audio_bus| directly to the encoder.
  296. std::unique_ptr<media::AudioBus> captured_audio =
  297. media::AudioBus::Create(audio_bus->channels(), audio_bus->frames());
  298. audio_bus->CopyTo(captured_audio.get());
  299. audio_data_callback_.Run(std::move(captured_audio), audio_capture_time);
  300. }
  301. void OnCaptureError(media::AudioCapturerSource::ErrorCode code,
  302. const std::string& message) override {
  303. if (!error_callback_.is_null())
  304. std::move(error_callback_).Run();
  305. }
  306. void OnCaptureMuted(bool is_muted) override {}
  307. const AudioDataCallback audio_data_callback_;
  308. base::OnceClosure error_callback_;
  309. };
  310. Session::Session(
  311. mojom::SessionParametersPtr session_params,
  312. const gfx::Size& max_resolution,
  313. mojo::PendingRemote<mojom::SessionObserver> observer,
  314. mojo::PendingRemote<mojom::ResourceProvider> resource_provider,
  315. mojo::PendingRemote<mojom::CastMessageChannel> outbound_channel,
  316. mojo::PendingReceiver<mojom::CastMessageChannel> inbound_channel,
  317. scoped_refptr<base::SingleThreadTaskRunner> io_task_runner)
  318. : session_params_(*session_params),
  319. observer_(std::move(observer)),
  320. resource_provider_(std::move(resource_provider)),
  321. message_dispatcher_(std::make_unique<MessageDispatcher>(
  322. std::move(outbound_channel),
  323. std::move(inbound_channel),
  324. base::BindRepeating(&Session::OnResponseParsingError,
  325. base::Unretained(this)))) {
  326. DCHECK(resource_provider_);
  327. mirror_settings_.SetResolutionConstraints(max_resolution.width(),
  328. max_resolution.height());
  329. resource_provider_->GetNetworkContext(
  330. network_context_.BindNewPipeAndPassReceiver());
  331. if (session_params->type != mojom::SessionType::AUDIO_ONLY &&
  332. io_task_runner) {
  333. mojo::PendingRemote<viz::mojom::Gpu> remote_gpu;
  334. resource_provider_->BindGpu(remote_gpu.InitWithNewPipeAndPassReceiver());
  335. gpu_ = viz::Gpu::Create(std::move(remote_gpu), io_task_runner);
  336. }
  337. network::mojom::URLLoaderFactoryParamsPtr params =
  338. network::mojom::URLLoaderFactoryParams::New();
  339. params->process_id = network::mojom::kBrowserProcessId;
  340. params->is_corb_enabled = false;
  341. mojo::PendingRemote<network::mojom::URLLoaderFactory> url_loader_factory;
  342. network_context_->CreateURLLoaderFactory(
  343. url_loader_factory.InitWithNewPipeAndPassReceiver(), std::move(params));
  344. setup_querier_ = std::make_unique<ReceiverSetupQuerier>(
  345. session_params_.receiver_address, std::move(url_loader_factory));
  346. }
  347. void Session::AsyncInitialize(AsyncInitializeDoneCB done_cb) {
  348. init_done_cb_ = std::move(done_cb);
  349. if (!gpu_) {
  350. // Post OnAsyncInitializeDone() instead of calling it directly to make sure
  351. // that CreateAndSendOffer() is always called asynchronously. This kind of
  352. // consistency is good for testing and reliability.
  353. auto runner = base::ThreadTaskRunnerHandle::Get();
  354. SupportedProfiles empty_profiles;
  355. runner->PostTask(FROM_HERE, base::BindOnce(&Session::OnAsyncInitializeDone,
  356. weak_factory_.GetWeakPtr(),
  357. std::move(empty_profiles)));
  358. return;
  359. }
  360. gpu_->CreateVideoEncodeAcceleratorProvider(
  361. vea_provider_.BindNewPipeAndPassReceiver());
  362. vea_provider_->GetVideoEncodeAcceleratorSupportedProfiles(base::BindOnce(
  363. &Session::OnAsyncInitializeDone, weak_factory_.GetWeakPtr()));
  364. }
  365. void Session::OnAsyncInitializeDone(const SupportedProfiles& profiles) {
  366. if (profiles.empty()) {
  367. // HW encoding is not supported.
  368. gpu_.reset();
  369. } else {
  370. supported_profiles_ = profiles;
  371. }
  372. DCHECK_EQ(state_, INITIALIZING);
  373. state_ = MIRRORING;
  374. CreateAndSendOffer();
  375. if (!init_done_cb_.is_null())
  376. std::move(init_done_cb_).Run();
  377. }
  378. Session::~Session() {
  379. StopSession();
  380. }
  381. void Session::ReportError(SessionError error) {
  382. UMA_HISTOGRAM_ENUMERATION("MediaRouter.MirroringService.SessionError", error);
  383. if (state_ == REMOTING) {
  384. media_remoter_->OnRemotingFailed(); // Try to fallback on mirroring.
  385. return;
  386. }
  387. // Report the error and stop this session.
  388. if (observer_)
  389. observer_->OnError(error);
  390. StopSession();
  391. }
  392. void Session::LogInfoMessage(const std::string& message) {
  393. if (observer_) {
  394. observer_->LogInfoMessage(message);
  395. }
  396. }
  397. void Session::LogErrorMessage(const std::string& message) {
  398. if (observer_) {
  399. observer_->LogErrorMessage(message);
  400. }
  401. }
  402. void Session::StopStreaming() {
  403. DVLOG(2) << __func__ << " state=" << state_;
  404. if (!cast_environment_)
  405. return;
  406. if (audio_input_device_) {
  407. audio_input_device_->Stop();
  408. audio_input_device_ = nullptr;
  409. }
  410. audio_capturing_callback_.reset();
  411. audio_stream_.reset();
  412. video_stream_.reset();
  413. cast_transport_.reset();
  414. cast_environment_ = nullptr;
  415. }
  416. void Session::StopSession() {
  417. DVLOG(1) << __func__;
  418. if (state_ == STOPPED)
  419. return;
  420. state_ = STOPPED;
  421. StopStreaming();
  422. // Notes on order: the media remoter needs to deregister itself from the
  423. // message dispatcher, which then needs to deregister from the resource
  424. // provider.
  425. media_remoter_.reset();
  426. message_dispatcher_.reset();
  427. setup_querier_.reset();
  428. weak_factory_.InvalidateWeakPtrs();
  429. audio_encode_thread_ = nullptr;
  430. video_encode_thread_ = nullptr;
  431. video_capture_client_.reset();
  432. resource_provider_.reset();
  433. gpu_.reset();
  434. if (observer_) {
  435. observer_->DidStop();
  436. observer_.reset();
  437. }
  438. }
  439. void Session::OnError(const std::string& message) {
  440. ReportError(SessionError::RTP_STREAM_ERROR);
  441. }
  442. void Session::RequestRefreshFrame() {
  443. DVLOG(3) << __func__;
  444. if (video_capture_client_)
  445. video_capture_client_->RequestRefreshFrame();
  446. }
  447. void Session::OnEncoderStatusChange(OperationalStatus status) {
  448. switch (status) {
  449. case OperationalStatus::STATUS_UNINITIALIZED:
  450. case OperationalStatus::STATUS_CODEC_REINIT_PENDING:
  451. // Not an error.
  452. // TODO(crbug.com/1015467): As an optimization, signal the client to pause
  453. // sending more frames until the state becomes STATUS_INITIALIZED again.
  454. case OperationalStatus::STATUS_INITIALIZED:
  455. break;
  456. case OperationalStatus::STATUS_INVALID_CONFIGURATION:
  457. case OperationalStatus::STATUS_UNSUPPORTED_CODEC:
  458. case OperationalStatus::STATUS_CODEC_INIT_FAILED:
  459. case OperationalStatus::STATUS_CODEC_RUNTIME_ERROR:
  460. ReportError(SessionError::ENCODING_ERROR);
  461. break;
  462. }
  463. }
  464. void Session::CreateVideoEncodeAccelerator(
  465. media::cast::ReceiveVideoEncodeAcceleratorCallback callback) {
  466. DCHECK_NE(state_, INITIALIZING);
  467. if (callback.is_null())
  468. return;
  469. std::unique_ptr<media::VideoEncodeAccelerator> mojo_vea;
  470. if (gpu_ && !supported_profiles_.empty()) {
  471. if (!vea_provider_) {
  472. gpu_->CreateVideoEncodeAcceleratorProvider(
  473. vea_provider_.BindNewPipeAndPassReceiver());
  474. }
  475. mojo::PendingRemote<media::mojom::VideoEncodeAccelerator> vea;
  476. vea_provider_->CreateVideoEncodeAccelerator(
  477. vea.InitWithNewPipeAndPassReceiver());
  478. // std::make_unique doesn't work to create a unique pointer of the subclass.
  479. mojo_vea = base::WrapUnique<media::VideoEncodeAccelerator>(
  480. new media::MojoVideoEncodeAccelerator(std::move(vea)));
  481. }
  482. std::move(callback).Run(base::ThreadTaskRunnerHandle::Get(),
  483. std::move(mojo_vea));
  484. }
  485. void Session::OnTransportStatusChanged(CastTransportStatus status) {
  486. DVLOG(1) << __func__ << ": status=" << status;
  487. switch (status) {
  488. case CastTransportStatus::TRANSPORT_STREAM_UNINITIALIZED:
  489. case CastTransportStatus::TRANSPORT_STREAM_INITIALIZED:
  490. return; // Not errors, do nothing.
  491. case CastTransportStatus::TRANSPORT_INVALID_CRYPTO_CONFIG:
  492. case CastTransportStatus::TRANSPORT_SOCKET_ERROR:
  493. ReportError(SessionError::CAST_TRANSPORT_ERROR);
  494. break;
  495. }
  496. }
  497. void Session::OnLoggingEventsReceived(
  498. std::unique_ptr<std::vector<FrameEvent>> frame_events,
  499. std::unique_ptr<std::vector<PacketEvent>> packet_events) {
  500. DCHECK(cast_environment_);
  501. cast_environment_->logger()->DispatchBatchOfEvents(std::move(frame_events),
  502. std::move(packet_events));
  503. }
  504. void Session::SetConstraints(const openscreen::cast::Answer& answer,
  505. FrameSenderConfig* audio_config,
  506. FrameSenderConfig* video_config) {
  507. const auto recommendations =
  508. openscreen::cast::capture_recommendations::GetRecommendations(answer);
  509. const auto& audio = recommendations.audio;
  510. const auto& video = recommendations.video;
  511. if (video_config) {
  512. // We use pixels instead of comparing width and height to allow for
  513. // differences in aspect ratio.
  514. const int current_pixels =
  515. mirror_settings_.max_width() * mirror_settings_.max_height();
  516. const int recommended_pixels = video.maximum.width * video.maximum.height;
  517. // Prioritize the stricter of the sender's and receiver's constraints.
  518. if (recommended_pixels < current_pixels) {
  519. // The resolution constraints here are used to generate the
  520. // media::VideoCaptureParams below.
  521. mirror_settings_.SetResolutionConstraints(video.maximum.width,
  522. video.maximum.height);
  523. }
  524. video_config->min_bitrate =
  525. std::max(video_config->min_bitrate, video.bit_rate_limits.minimum);
  526. video_config->start_bitrate = video_config->min_bitrate;
  527. video_config->max_bitrate =
  528. std::min(video_config->max_bitrate, video.bit_rate_limits.maximum);
  529. video_config->max_playout_delay =
  530. std::min(video_config->max_playout_delay,
  531. base::Milliseconds(video.max_delay.count()));
  532. video_config->max_frame_rate =
  533. std::min(video_config->max_frame_rate,
  534. static_cast<double>(video.maximum.frame_rate));
  535. // We only do sender-side letterboxing if the receiver doesn't support it.
  536. mirror_settings_.SetSenderSideLetterboxingEnabled(!video.supports_scaling);
  537. }
  538. if (audio_config) {
  539. audio_config->min_bitrate =
  540. std::max(audio_config->min_bitrate, audio.bit_rate_limits.minimum);
  541. audio_config->start_bitrate = audio_config->min_bitrate;
  542. audio_config->max_bitrate =
  543. std::min(audio_config->max_bitrate, audio.bit_rate_limits.maximum);
  544. audio_config->max_playout_delay =
  545. std::min(audio_config->max_playout_delay,
  546. base::Milliseconds(audio.max_delay.count()));
  547. // Currently, Chrome only supports stereo, so audio.max_channels is ignored.
  548. }
  549. }
  550. void Session::OnAnswer(const std::vector<FrameSenderConfig>& audio_configs,
  551. const std::vector<FrameSenderConfig>& video_configs,
  552. const ReceiverResponse& response) {
  553. if (state_ == STOPPED)
  554. return;
  555. if (response.type() == ResponseType::UNKNOWN) {
  556. ReportError(SessionError::ANSWER_TIME_OUT);
  557. return;
  558. }
  559. DCHECK_EQ(ResponseType::ANSWER, response.type());
  560. if (!response.valid()) {
  561. ReportError(SessionError::ANSWER_NOT_OK);
  562. return;
  563. }
  564. const openscreen::cast::Answer& answer = response.answer();
  565. if (answer.send_indexes.size() != answer.ssrcs.size()) {
  566. ReportError(SessionError::ANSWER_MISMATCHED_SSRC_LENGTH);
  567. return;
  568. }
  569. // Select Audio/Video config from ANSWER.
  570. bool has_audio = false;
  571. bool has_video = false;
  572. FrameSenderConfig audio_config;
  573. FrameSenderConfig video_config;
  574. const int video_start_idx = audio_configs.size();
  575. const int video_idx_bound = video_configs.size() + video_start_idx;
  576. for (size_t i = 0; i < answer.send_indexes.size(); ++i) {
  577. if (answer.send_indexes[i] < 0 ||
  578. answer.send_indexes[i] >= video_idx_bound) {
  579. ReportError(SessionError::ANSWER_SELECT_INVALID_INDEX);
  580. return;
  581. }
  582. if (answer.send_indexes[i] < video_start_idx) {
  583. // Audio
  584. if (has_audio) {
  585. ReportError(SessionError::ANSWER_SELECT_MULTIPLE_AUDIO);
  586. return;
  587. }
  588. audio_config = audio_configs[answer.send_indexes[i]];
  589. audio_config.receiver_ssrc = answer.ssrcs[i];
  590. has_audio = true;
  591. } else {
  592. // Video
  593. if (has_video) {
  594. ReportError(SessionError::ANSWER_SELECT_MULTIPLE_VIDEO);
  595. return;
  596. }
  597. video_config = video_configs[answer.send_indexes[i] - video_start_idx];
  598. video_config.receiver_ssrc = answer.ssrcs[i];
  599. video_config.video_codec_params.number_of_encode_threads =
  600. NumberOfEncodeThreads();
  601. has_video = true;
  602. }
  603. }
  604. if (!has_audio && !has_video) {
  605. ReportError(SessionError::ANSWER_NO_AUDIO_OR_VIDEO);
  606. return;
  607. }
  608. // Set constraints from ANSWER message.
  609. SetConstraints(answer, has_audio ? &audio_config : nullptr,
  610. has_video ? &video_config : nullptr);
  611. // Start streaming.
  612. const bool initially_starting_session =
  613. !audio_encode_thread_ && !video_encode_thread_;
  614. if (initially_starting_session) {
  615. audio_encode_thread_ = base::ThreadPool::CreateSingleThreadTaskRunner(
  616. {base::TaskPriority::USER_BLOCKING,
  617. base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
  618. base::SingleThreadTaskRunnerThreadMode::DEDICATED);
  619. video_encode_thread_ = base::ThreadPool::CreateSingleThreadTaskRunner(
  620. {base::TaskPriority::USER_BLOCKING,
  621. base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
  622. base::SingleThreadTaskRunnerThreadMode::DEDICATED);
  623. }
  624. cast_environment_ = new media::cast::CastEnvironment(
  625. base::DefaultTickClock::GetInstance(),
  626. base::ThreadTaskRunnerHandle::Get(), audio_encode_thread_,
  627. video_encode_thread_);
  628. auto udp_client = std::make_unique<UdpSocketClient>(
  629. net::IPEndPoint(session_params_.receiver_address, answer.udp_port),
  630. network_context_.get(),
  631. base::BindOnce(&Session::ReportError, weak_factory_.GetWeakPtr(),
  632. SessionError::CAST_TRANSPORT_ERROR));
  633. cast_transport_ = media::cast::CastTransport::Create(
  634. cast_environment_->Clock(), kSendEventsInterval,
  635. std::make_unique<TransportClient>(this), std::move(udp_client),
  636. base::ThreadTaskRunnerHandle::Get());
  637. if (state_ == REMOTING) {
  638. DCHECK(media_remoter_);
  639. DCHECK(audio_config.rtp_payload_type == RtpPayloadType::REMOTE_AUDIO ||
  640. video_config.rtp_payload_type == RtpPayloadType::REMOTE_VIDEO);
  641. media_remoter_->StartRpcMessaging(cast_environment_, cast_transport_.get(),
  642. audio_config, video_config);
  643. } else /* MIRRORING */ {
  644. if (has_audio) {
  645. auto audio_sender = std::make_unique<media::cast::AudioSender>(
  646. cast_environment_, audio_config,
  647. base::BindOnce(&Session::OnEncoderStatusChange,
  648. weak_factory_.GetWeakPtr()),
  649. cast_transport_.get());
  650. audio_stream_ = std::make_unique<AudioRtpStream>(
  651. std::move(audio_sender), weak_factory_.GetWeakPtr());
  652. DCHECK(!audio_capturing_callback_);
  653. // TODO(crbug.com/1015467): Eliminate the thread hops. The audio data is
  654. // thread-hopped from the audio thread, and later thread-hopped again to
  655. // the encoding thread.
  656. audio_capturing_callback_ = std::make_unique<AudioCapturingCallback>(
  657. media::BindToCurrentLoop(base::BindRepeating(
  658. &AudioRtpStream::InsertAudio, audio_stream_->AsWeakPtr())),
  659. base::BindOnce(&Session::ReportError, weak_factory_.GetWeakPtr(),
  660. SessionError::AUDIO_CAPTURE_ERROR));
  661. audio_input_device_ = new media::AudioInputDevice(
  662. std::make_unique<CapturedAudioInput>(base::BindRepeating(
  663. &Session::CreateAudioStream, base::Unretained(this))),
  664. media::AudioInputDevice::Purpose::kLoopback,
  665. media::AudioInputDevice::DeadStreamDetection::kEnabled);
  666. audio_input_device_->Initialize(mirror_settings_.GetAudioCaptureParams(),
  667. audio_capturing_callback_.get());
  668. audio_input_device_->Start();
  669. }
  670. if (has_video) {
  671. auto video_sender = std::make_unique<media::cast::VideoSender>(
  672. cast_environment_, video_config,
  673. base::BindRepeating(&Session::OnEncoderStatusChange,
  674. weak_factory_.GetWeakPtr()),
  675. base::BindRepeating(&Session::CreateVideoEncodeAccelerator,
  676. weak_factory_.GetWeakPtr()),
  677. cast_transport_.get(),
  678. base::BindRepeating(&Session::SetTargetPlayoutDelay,
  679. weak_factory_.GetWeakPtr()),
  680. base::BindRepeating(&Session::ProcessFeedback,
  681. weak_factory_.GetWeakPtr()));
  682. video_stream_ = std::make_unique<VideoRtpStream>(
  683. std::move(video_sender), weak_factory_.GetWeakPtr());
  684. if (!video_capture_client_) {
  685. mojo::PendingRemote<media::mojom::VideoCaptureHost> video_host;
  686. resource_provider_->GetVideoCaptureHost(
  687. video_host.InitWithNewPipeAndPassReceiver());
  688. video_capture_client_ = std::make_unique<VideoCaptureClient>(
  689. mirror_settings_.GetVideoCaptureParams(), std::move(video_host));
  690. video_capture_client_->Start(
  691. base::BindRepeating(&VideoRtpStream::InsertVideoFrame,
  692. video_stream_->AsWeakPtr()),
  693. base::BindOnce(&Session::ReportError, weak_factory_.GetWeakPtr(),
  694. SessionError::VIDEO_CAPTURE_ERROR));
  695. } else {
  696. video_capture_client_->Resume(base::BindRepeating(
  697. &VideoRtpStream::InsertVideoFrame, video_stream_->AsWeakPtr()));
  698. }
  699. }
  700. if (media_remoter_)
  701. media_remoter_->OnMirroringResumed();
  702. }
  703. if (initially_starting_session &&
  704. ShouldQueryForRemotingCapabilities(session_params_.receiver_model_name)) {
  705. QueryCapabilitiesForRemoting();
  706. }
  707. if (initially_starting_session && observer_)
  708. observer_->DidStart();
  709. }
  710. void Session::OnResponseParsingError(const std::string& error_message) {
  711. LogErrorMessage(base::StrCat({"MessageDispatcher error: ", error_message}));
  712. }
  713. void Session::CreateAudioStream(
  714. mojo::PendingRemote<mojom::AudioStreamCreatorClient> client,
  715. const media::AudioParameters& params,
  716. uint32_t shared_memory_count) {
  717. resource_provider_->CreateAudioStream(std::move(client), params,
  718. shared_memory_count);
  719. }
  720. void Session::SetTargetPlayoutDelay(base::TimeDelta playout_delay) {
  721. if (audio_stream_)
  722. audio_stream_->SetTargetPlayoutDelay(playout_delay);
  723. if (video_stream_)
  724. video_stream_->SetTargetPlayoutDelay(playout_delay);
  725. }
  726. void Session::ProcessFeedback(const media::VideoCaptureFeedback& feedback) {
  727. if (video_capture_client_) {
  728. video_capture_client_->ProcessFeedback(feedback);
  729. }
  730. }
  731. // TODO(issuetracker.google.com/159352836): Refactor to use libcast's
  732. // OFFER message format.
  733. void Session::CreateAndSendOffer() {
  734. DCHECK_NE(state_, STOPPED);
  735. DCHECK_NE(state_, INITIALIZING);
  736. // The random AES key and initialization vector pair used by all streams in
  737. // this session.
  738. const std::string aes_key = MakeRandomString(16); // AES-128.
  739. const std::string aes_iv = MakeRandomString(16); // AES has 128-bit blocks.
  740. std::vector<FrameSenderConfig> audio_configs;
  741. std::vector<FrameSenderConfig> video_configs;
  742. // Generate stream list with supported audio / video configs.
  743. base::Value::List stream_list;
  744. int stream_index = 0;
  745. if (session_params_.type != SessionType::VIDEO_ONLY) {
  746. const int32_t audio_ssrc = base::RandInt(kAudioSsrcMin, kAudioSsrcMax);
  747. if (state_ == MIRRORING) {
  748. FrameSenderConfig config = MirrorSettings::GetDefaultAudioConfig(
  749. RtpPayloadType::AUDIO_OPUS, Codec::CODEC_AUDIO_OPUS);
  750. AddSenderConfig(audio_ssrc, config, aes_key, aes_iv, session_params_,
  751. &audio_configs);
  752. AddStreamObject(stream_index++, "OPUS", audio_configs.back(),
  753. mirror_settings_, stream_list);
  754. } else /* REMOTING */ {
  755. FrameSenderConfig config = MirrorSettings::GetDefaultAudioConfig(
  756. RtpPayloadType::REMOTE_AUDIO, Codec::CODEC_AUDIO_REMOTE);
  757. AddSenderConfig(audio_ssrc, config, aes_key, aes_iv, session_params_,
  758. &audio_configs);
  759. AddStreamObject(stream_index++, "REMOTE_AUDIO", audio_configs.back(),
  760. mirror_settings_, stream_list);
  761. }
  762. }
  763. if (session_params_.type != SessionType::AUDIO_ONLY) {
  764. const int32_t video_ssrc = base::RandInt(kVideoSsrcMin, kVideoSsrcMax);
  765. if (state_ == MIRRORING) {
  766. // First, check if hardware VP8 and H264 are available.
  767. if (media::cast::ExternalVideoEncoder::IsRecommended(
  768. Codec::CODEC_VIDEO_VP8, session_params_.receiver_model_name,
  769. supported_profiles_)) {
  770. FrameSenderConfig config = MirrorSettings::GetDefaultVideoConfig(
  771. RtpPayloadType::VIDEO_VP8, Codec::CODEC_VIDEO_VP8);
  772. config.use_external_encoder = true;
  773. AddSenderConfig(video_ssrc, config, aes_key, aes_iv, session_params_,
  774. &video_configs);
  775. AddStreamObject(stream_index++, "VP8", video_configs.back(),
  776. mirror_settings_, stream_list);
  777. }
  778. if (media::cast::ExternalVideoEncoder::IsRecommended(
  779. Codec::CODEC_VIDEO_H264, session_params_.receiver_model_name,
  780. supported_profiles_)) {
  781. FrameSenderConfig config = MirrorSettings::GetDefaultVideoConfig(
  782. RtpPayloadType::VIDEO_H264, Codec::CODEC_VIDEO_H264);
  783. config.use_external_encoder = true;
  784. AddSenderConfig(video_ssrc, config, aes_key, aes_iv, session_params_,
  785. &video_configs);
  786. AddStreamObject(stream_index++, "H264", video_configs.back(),
  787. mirror_settings_, stream_list);
  788. }
  789. // Then add software AV1 and VP9 if enabled.
  790. // TODO(https://crbug.com/1311770): hardware VP9 encoding should be added.
  791. if (mirroring::features::IsCastStreamingAV1Enabled()) {
  792. FrameSenderConfig config = MirrorSettings::GetDefaultVideoConfig(
  793. RtpPayloadType::VIDEO_AV1, Codec::CODEC_VIDEO_AV1);
  794. AddSenderConfig(video_ssrc, config, aes_key, aes_iv, session_params_,
  795. &video_configs);
  796. AddStreamObject(stream_index++, "AV1", video_configs.back(),
  797. mirror_settings_, stream_list);
  798. }
  799. if (base::FeatureList::IsEnabled(features::kCastStreamingVp9)) {
  800. FrameSenderConfig config = MirrorSettings::GetDefaultVideoConfig(
  801. RtpPayloadType::VIDEO_VP9, Codec::CODEC_VIDEO_VP9);
  802. AddSenderConfig(video_ssrc, config, aes_key, aes_iv, session_params_,
  803. &video_configs);
  804. AddStreamObject(stream_index++, "VP9", video_configs.back(),
  805. mirror_settings_, stream_list);
  806. }
  807. // Worst case, default to offering software VP8.
  808. if (video_configs.empty()) {
  809. FrameSenderConfig config = MirrorSettings::GetDefaultVideoConfig(
  810. RtpPayloadType::VIDEO_VP8, Codec::CODEC_VIDEO_VP8);
  811. AddSenderConfig(video_ssrc, config, aes_key, aes_iv, session_params_,
  812. &video_configs);
  813. AddStreamObject(stream_index++, "VP8", video_configs.back(),
  814. mirror_settings_, stream_list);
  815. }
  816. } else /* REMOTING */ {
  817. FrameSenderConfig config = MirrorSettings::GetDefaultVideoConfig(
  818. RtpPayloadType::REMOTE_VIDEO, Codec::CODEC_VIDEO_REMOTE);
  819. AddSenderConfig(video_ssrc, config, aes_key, aes_iv, session_params_,
  820. &video_configs);
  821. AddStreamObject(stream_index++, "REMOTE_VIDEO", video_configs.back(),
  822. mirror_settings_, stream_list);
  823. }
  824. }
  825. DCHECK(!audio_configs.empty() || !video_configs.empty());
  826. // Assemble the OFFER message.
  827. base::Value::Dict offer;
  828. offer.Set("castMode", state_ == MIRRORING ? "mirroring" : "remoting");
  829. offer.Set("receiverGetStatus", true);
  830. offer.Set("supportedStreams", std::move(stream_list));
  831. const int32_t sequence_number = message_dispatcher_->GetNextSeqNumber();
  832. base::Value::Dict offer_message;
  833. offer_message.Set("type", "OFFER");
  834. offer_message.Set("seqNum", sequence_number);
  835. offer_message.Set("offer", std::move(offer));
  836. mojom::CastMessagePtr message_to_receiver = mojom::CastMessage::New();
  837. message_to_receiver->message_namespace = mojom::kWebRtcNamespace;
  838. const bool did_serialize_offer = base::JSONWriter::Write(
  839. offer_message, &message_to_receiver->json_format_data);
  840. DCHECK(did_serialize_offer);
  841. message_dispatcher_->RequestReply(
  842. std::move(message_to_receiver), ResponseType::ANSWER, sequence_number,
  843. kOfferAnswerExchangeTimeout,
  844. base::BindOnce(&Session::OnAnswer, base::Unretained(this), audio_configs,
  845. video_configs));
  846. }
  847. void Session::ConnectToRemotingSource(
  848. mojo::PendingRemote<media::mojom::Remoter> remoter,
  849. mojo::PendingReceiver<media::mojom::RemotingSource> receiver) {
  850. resource_provider_->ConnectToRemotingSource(std::move(remoter),
  851. std::move(receiver));
  852. }
  853. void Session::RequestRemotingStreaming() {
  854. DCHECK(media_remoter_);
  855. DCHECK_EQ(MIRRORING, state_);
  856. if (video_capture_client_)
  857. video_capture_client_->Pause();
  858. StopStreaming();
  859. state_ = REMOTING;
  860. CreateAndSendOffer();
  861. }
  862. void Session::RestartMirroringStreaming() {
  863. if (state_ != REMOTING)
  864. return;
  865. StopStreaming();
  866. state_ = MIRRORING;
  867. CreateAndSendOffer();
  868. }
  869. void Session::QueryCapabilitiesForRemoting() {
  870. DCHECK(!media_remoter_);
  871. const int32_t sequence_number = message_dispatcher_->GetNextSeqNumber();
  872. base::Value::Dict query;
  873. query.Set("type", "GET_CAPABILITIES");
  874. query.Set("seqNum", sequence_number);
  875. mojom::CastMessagePtr query_message = mojom::CastMessage::New();
  876. query_message->message_namespace = mojom::kWebRtcNamespace;
  877. const bool did_serialize_query =
  878. base::JSONWriter::Write(query, &query_message->json_format_data);
  879. DCHECK(did_serialize_query);
  880. message_dispatcher_->RequestReply(
  881. std::move(query_message), ResponseType::CAPABILITIES_RESPONSE,
  882. sequence_number, kGetCapabilitiesTimeout,
  883. base::BindOnce(&Session::OnCapabilitiesResponse, base::Unretained(this)));
  884. }
  885. void Session::OnCapabilitiesResponse(const ReceiverResponse& response) {
  886. if (state_ == STOPPED)
  887. return;
  888. if (!response.valid()) {
  889. if (response.error()) {
  890. LogErrorMessage(base::StringPrintf(
  891. "Remoting is not supported. Error code: %d, description: %s, "
  892. "details: %s",
  893. response.error()->code, response.error()->description.c_str(),
  894. response.error()->details.c_str()));
  895. } else {
  896. LogErrorMessage("Remoting is not supported. Bad CAPABILITIES_RESPONSE.");
  897. }
  898. return;
  899. }
  900. // Check if the remoting version used in the receiver is supported or not.
  901. int remoting_version = response.capabilities().remoting;
  902. // For backwards-compatibility, if the remoting version field was not set,
  903. // assume it is 1.
  904. if (remoting_version == ReceiverCapability::kRemotingVersionUnknown) {
  905. remoting_version = 1;
  906. }
  907. if (remoting_version > kSupportedRemotingVersion) {
  908. LogErrorMessage(
  909. base::StringPrintf("Remoting is not supported. The receiver's remoting "
  910. "version (%d) is not supported by the sender (%d).",
  911. remoting_version, kSupportedRemotingVersion));
  912. return;
  913. }
  914. const std::vector<std::string>& caps = response.capabilities().media_caps;
  915. std::string build_version;
  916. std::string friendly_name;
  917. if (setup_querier_) {
  918. build_version = setup_querier_->build_version();
  919. friendly_name = setup_querier_->friendly_name();
  920. }
  921. media_remoter_ = std::make_unique<MediaRemoter>(
  922. this,
  923. ToRemotingSinkMetadata(caps, friendly_name, session_params_,
  924. build_version),
  925. message_dispatcher_.get());
  926. }
  927. } // namespace mirroring