Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(331)

Side by Side Diff: chrome/browser/media/router/discovery/mdns/cast_media_sink_service.cc

Issue 2927833002: [Media Router] Add CastMediaSinkService (Closed)
Patch Set: add unit tests Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 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
5 #include "chrome/browser/media/router/discovery/mdns/cast_media_sink_service.h"
6
7 #include "chrome/browser/browser_process.h"
8 #include "chrome/common/media_router/discovery/media_sink_internal.h"
9 #include "components/cast_channel/cast_socket_service.h"
10 #include "components/cast_channel/cast_socket_service_factory.h"
11 #include "components/net_log/chrome_net_log.h"
12 #include "content/public/common/content_client.h"
13 #include "net/base/host_port_pair.h"
14 #include "net/base/ip_address.h"
15
16 namespace {
17 // mDNS service types.
18 const char kCastServiceType[] = "_googlecast._tcp.local";
19
20 enum ErrorType {
21 NONE,
22 NOT_CAST_DEVICE,
23 MISSING_ID,
24 MISSING_FRIENDLY_NAME,
25 MISSING_OR_INVALID_IP_ADDRESS,
26 MISSING_OR_INVALID_PORT,
27 };
28
29 ErrorType CreateCastMediaSink(const media_router::DnsSdService& service,
30 int channel_id,
31 bool audio_only,
32 media_router::MediaSinkInternal* cast_sink) {
33 DCHECK(cast_sink);
34 if (service.service_name.find(kCastServiceType) == std::string::npos)
35 return ErrorType::NOT_CAST_DEVICE;
36
37 net::IPAddress ip_address;
38 if (!ip_address.AssignFromIPLiteral(service.ip_address))
39 return ErrorType::MISSING_OR_INVALID_IP_ADDRESS;
40
41 std::map<std::string, std::string> service_data;
42 for (const auto& item : service.service_data) {
43 // |item| format should be "id=xxxxxx", etc.
44 size_t split_idx = item.find('=');
45 if (split_idx == std::string::npos)
46 continue;
47
48 std::string key = item.substr(0, split_idx);
49 std::string val = item.substr(split_idx + 1);
50 service_data[key] = val;
51 }
52
53 // When use this "sink" within browser, please note it will have a different
54 // ID when it is sent to the extension, because it derives a different sink ID
55 // using the given sink ID.
56 std::string unique_id = service_data["id"];
57 if (unique_id.empty())
58 return ErrorType::MISSING_ID;
59 std::string friendly_name = service_data["fn"];
60 if (friendly_name.empty())
61 return ErrorType::MISSING_FRIENDLY_NAME;
62 media_router::MediaSink sink(unique_id, friendly_name,
63 media_router::MediaSink::IconType::CAST);
64
65 media_router::CastSinkExtraData extra_data;
66 extra_data.ip_address = ip_address;
67 extra_data.model_name = service_data["md"];
68 extra_data.capabilities = cast_channel::CastDeviceCapability::AUDIO_OUT;
69 if (!audio_only)
70 extra_data.capabilities |= cast_channel::CastDeviceCapability::VIDEO_OUT;
71 extra_data.cast_channel_id = channel_id;
72
73 cast_sink->set_sink(sink);
74 cast_sink->set_cast_data(extra_data);
75
76 return ErrorType::NONE;
77 }
78
79 } // namespace
80
81 namespace media_router {
82
83 CastMediaSinkService::CastMediaSinkService(
84 const OnSinksDiscoveredCallback& callback,
85 content::BrowserContext* browser_context)
86 : MediaSinkServiceBase(callback) {
87 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
88 cast_socket_service_ = cast_channel::CastSocketServiceFactory::GetInstance()
imcheng 2017/06/15 00:50:59 Would we need to add a DependsOn from MediaRouterF
zhaobin 2017/06/20 02:30:28 It seems fine since we call cast_channel::CastSock
89 ->GetForBrowserContext(browser_context);
90 DCHECK(cast_socket_service_);
91 }
92
93 CastMediaSinkService::~CastMediaSinkService() {}
94
95 void CastMediaSinkService::Start() {
96 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
97 if (dns_sd_registry_)
98 return;
99
100 dns_sd_registry_ = test_dns_sd_registry_ ? test_dns_sd_registry_
101 : DnsSdRegistry::GetInstance();
102 dns_sd_registry_->AddObserver(this);
103 dns_sd_registry_->RegisterDnsSdListener(kCastServiceType);
104 MediaSinkServiceBase::StartTimer();
105 }
106
107 void CastMediaSinkService::Stop() {
108 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
109 if (!dns_sd_registry_)
110 return;
111
112 dns_sd_registry_->UnregisterDnsSdListener(kCastServiceType);
113 dns_sd_registry_->RemoveObserver(this);
114 dns_sd_registry_ = nullptr;
115 MediaSinkServiceBase::StopTimer();
116 }
117
118 void CastMediaSinkService::SetDnsSdRegistryForTesting(DnsSdRegistry* registry) {
119 DCHECK(!test_dns_sd_registry_);
120 test_dns_sd_registry_ = registry;
121 }
122
123 void CastMediaSinkService::SetCastSocketServiceForTesting(
124 cast_channel::CastSocketService* cast_socket_service) {
125 cast_socket_service_ = cast_socket_service;
imcheng 2017/06/15 00:50:56 Add a DCHECK to be consistent with above.
zhaobin 2017/06/20 02:30:28 Moved this to a ctor used by test.
126 }
127
128 void CastMediaSinkService::OnDnsSdEvent(
129 const std::string& service_type,
130 const DnsSdRegistry::DnsSdServiceList& services) {
131 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
132 DVLOG(2) << "CastMediaSinkService::OnDnsSdEvent found " << services.size()
133 << " services";
134
135 current_sinks_.clear();
136 current_services_ = services;
137
138 for (const auto& service : services) {
139 net::IPAddress ip_address;
140 if (!ip_address.AssignFromIPLiteral(service.ip_address)) {
141 DVLOG(2) << "Invalid ip_addresss: " << service.ip_address;
142 continue;
143 }
144 net::HostPortPair host_port_pair =
145 net::HostPortPair::FromString(service.service_host_port);
146
147 content::BrowserThread::PostTask(
148 content::BrowserThread::IO, FROM_HERE,
149 base::Bind(&CastMediaSinkService::OpenChannelOnIOThread, this, service,
150 net::IPEndPoint(ip_address, host_port_pair.port())));
151 }
152 }
153
154 void CastMediaSinkService::OpenChannelOnIOThread(
155 const DnsSdService& service,
156 const net::IPEndPoint& ip_endpoint) {
157 cast_socket_service_->OpenSocket(
imcheng 2017/06/15 00:50:57 What if the socket already exists (opened by brows
zhaobin 2017/06/20 02:30:28 Handled inside CastSocketService::OpenSocket().
158 ip_endpoint, g_browser_process->net_log(),
159 base::Bind(&CastMediaSinkService::OnChannelOpenedOnIOThread, this,
160 service));
161 }
162
163 void CastMediaSinkService::OnChannelOpenedOnIOThread(
164 const DnsSdService& service,
165 int channel_id,
imcheng 2017/06/15 00:50:56 Would it be possible to pass back the CastSocket i
zhaobin 2017/06/20 02:30:28 DISALLOW_COPY_AND_ASSIGN(CastSocketImpl); Do not
166 cast_channel::ChannelError channel_error) {
167 if (channel_error != cast_channel::ChannelError::NONE) {
168 DVLOG(2) << "Fail to open channel " << service.ip_address << ": "
169 << service.service_host_port
170 << " [ChannelError]: " << (int)channel_error;
171 return;
172 }
173
174 auto* socket = cast_socket_service_->GetSocket(channel_id);
175 if (!socket) {
176 DVLOG(2) << "Fail to find socket with [channel_id]: " << channel_id;
177 return;
178 }
179
180 content::BrowserThread::PostTask(
181 content::BrowserThread::UI, FROM_HERE,
182 base::Bind(&CastMediaSinkService::OnChannelOpenedOnUIThread, this,
183 service, channel_id, socket->audio_only()));
184 }
185
186 void CastMediaSinkService::OnChannelOpenedOnUIThread(
187 const DnsSdService& service,
188 int channel_id,
189 bool audio_only) {
190 MediaSinkInternal sink;
imcheng 2017/06/15 00:50:56 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
zhaobin 2017/06/20 02:30:28 Done.
191 ErrorType error = CreateCastMediaSink(service, channel_id, audio_only, &sink);
192 if (error != ErrorType::NONE) {
193 DVLOG(2) << "Fail to create Cast device [error]: " << error;
194 return;
195 }
196
197 if (!base::ContainsValue(current_services_, service)) {
imcheng 2017/06/15 00:50:56 This check means we would drop the sink if we some
zhaobin 2017/06/20 02:30:28 DialMediaSinkService has similar logic so copied i
198 DVLOG(2) << "Service data not found in current service data list...";
199 return;
200 }
201
202 DVLOG(2) << "Ading sink to current_sinks_ [id]: " << sink.sink().id();
203 current_sinks_.insert(sink);
204 MediaSinkServiceBase::RestartTimer();
205 }
206
207 } // namespace media_router
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698