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

Side by Side Diff: media/remoting/remoting_renderer_controller_unittest.cc

Issue 2457563002: Media Remoting: Add remoting control logic for encrypted contents. (Closed)
Patch Set: Rebase Only. Created 4 years, 1 month 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
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "media/remoting/remoting_controller.h" 5 #include "media/remoting/remoting_renderer_controller.h"
6 6
7 #include "base/callback.h" 7 #include "base/callback.h"
8 #include "base/message_loop/message_loop.h" 8 #include "base/message_loop/message_loop.h"
9 #include "base/run_loop.h" 9 #include "base/run_loop.h"
10 #include "media/base/audio_decoder_config.h" 10 #include "media/base/audio_decoder_config.h"
11 #include "media/base/cdm_config.h"
11 #include "media/base/limits.h" 12 #include "media/base/limits.h"
12 #include "media/base/media_util.h" 13 #include "media/base/media_util.h"
13 #include "media/base/video_decoder_config.h" 14 #include "media/base/video_decoder_config.h"
15 #include "media/remoting/remoting_cdm.h"
14 #include "mojo/public/cpp/bindings/strong_binding.h" 16 #include "mojo/public/cpp/bindings/strong_binding.h"
15 #include "testing/gtest/include/gtest/gtest.h" 17 #include "testing/gtest/include/gtest/gtest.h"
16 18
17 namespace media { 19 namespace media {
18 namespace { 20 namespace {
19 21
20 constexpr gfx::Size kCodedSize(320, 240); 22 constexpr gfx::Size kCodedSize(320, 240);
21 constexpr gfx::Rect kVisibleRect(320, 240); 23 constexpr gfx::Rect kVisibleRect(320, 240);
22 constexpr gfx::Size kNaturalSize(320, 240); 24 constexpr gfx::Size kNaturalSize(320, 240);
23 25
24 PipelineMetadata defaultMetadata() { 26 PipelineMetadata DefaultMetadata() {
25 PipelineMetadata data; 27 PipelineMetadata data;
26 data.has_audio = true; 28 data.has_audio = true;
27 data.has_video = true; 29 data.has_video = true;
28 data.video_decoder_config = VideoDecoderConfig( 30 data.video_decoder_config = VideoDecoderConfig(
29 kCodecVP8, VP8PROFILE_ANY, VideoPixelFormat::PIXEL_FORMAT_I420, 31 kCodecVP8, VP8PROFILE_ANY, VideoPixelFormat::PIXEL_FORMAT_I420,
30 ColorSpace::COLOR_SPACE_SD_REC601, kCodedSize, kVisibleRect, kNaturalSize, 32 ColorSpace::COLOR_SPACE_SD_REC601, kCodedSize, kVisibleRect, kNaturalSize,
31 EmptyExtraData(), Unencrypted()); 33 EmptyExtraData(), Unencrypted());
32 data.audio_decoder_config = AudioDecoderConfig( 34 data.audio_decoder_config = AudioDecoderConfig(
33 kCodecOpus, SampleFormat::kSampleFormatU8, 35 kCodecOpus, SampleFormat::kSampleFormatU8,
34 ChannelLayout::CHANNEL_LAYOUT_MONO, limits::kMinSampleRate, 36 ChannelLayout::CHANNEL_LAYOUT_MONO, limits::kMinSampleRate,
35 EmptyExtraData(), Unencrypted()); 37 EmptyExtraData(), Unencrypted());
36 return data; 38 return data;
37 } 39 }
38 40
41 PipelineMetadata EncryptedMetadata() {
42 PipelineMetadata data;
43 data.has_audio = true;
44 data.has_video = true;
45 data.video_decoder_config = VideoDecoderConfig(
46 kCodecVP8, VP8PROFILE_ANY, VideoPixelFormat::PIXEL_FORMAT_I420,
47 ColorSpace::COLOR_SPACE_SD_REC601, kCodedSize, kVisibleRect, kNaturalSize,
48 EmptyExtraData(), AesCtrEncryptionScheme());
49 data.audio_decoder_config = AudioDecoderConfig(
50 kCodecOpus, SampleFormat::kSampleFormatU8,
51 ChannelLayout::CHANNEL_LAYOUT_MONO, limits::kMinSampleRate,
52 EmptyExtraData(), Unencrypted());
53 return data;
54 }
55
39 class FakeRemoter final : public mojom::Remoter { 56 class FakeRemoter final : public mojom::Remoter {
40 public: 57 public:
41 // |start_will_fail| indicates whether starting remoting will fail. 58 // |start_will_fail| indicates whether starting remoting will fail.
42 FakeRemoter(mojom::RemotingSourcePtr source, bool start_will_fail) 59 FakeRemoter(mojom::RemotingSourcePtr source, bool start_will_fail)
43 : source_(std::move(source)), start_will_fail_(start_will_fail) {} 60 : source_(std::move(source)),
61 start_will_fail_(start_will_fail),
62 weak_factory_(this) {}
44 ~FakeRemoter() override {} 63 ~FakeRemoter() override {}
45 64
46 // mojom::Remoter implementations. 65 // mojom::Remoter implementations.
47 void Start() override { 66 void Start() override {
48 if (start_will_fail_) { 67 if (start_will_fail_) {
49 base::ThreadTaskRunnerHandle::Get()->PostTask( 68 base::ThreadTaskRunnerHandle::Get()->PostTask(
50 FROM_HERE, 69 FROM_HERE,
51 base::Bind(&FakeRemoter::StartFailed, base::Unretained(this))); 70 base::Bind(&FakeRemoter::StartFailed, weak_factory_.GetWeakPtr()));
52 } else { 71 } else {
53 base::ThreadTaskRunnerHandle::Get()->PostTask( 72 base::ThreadTaskRunnerHandle::Get()->PostTask(
54 FROM_HERE, base::Bind(&FakeRemoter::Started, base::Unretained(this))); 73 FROM_HERE,
74 base::Bind(&FakeRemoter::Started, weak_factory_.GetWeakPtr()));
55 } 75 }
56 } 76 }
57 77
58 void StartDataStreams( 78 void StartDataStreams(
59 mojo::ScopedDataPipeConsumerHandle audio_pipe, 79 mojo::ScopedDataPipeConsumerHandle audio_pipe,
60 mojo::ScopedDataPipeConsumerHandle video_pipe, 80 mojo::ScopedDataPipeConsumerHandle video_pipe,
61 mojom::RemotingDataStreamSenderRequest audio_sender_request, 81 mojom::RemotingDataStreamSenderRequest audio_sender_request,
62 mojom::RemotingDataStreamSenderRequest video_sender_request) override {} 82 mojom::RemotingDataStreamSenderRequest video_sender_request) override {}
63 83
64 void Stop(mojom::RemotingStopReason reason) override { 84 void Stop(mojom::RemotingStopReason reason) override {
65 base::ThreadTaskRunnerHandle::Get()->PostTask( 85 base::ThreadTaskRunnerHandle::Get()->PostTask(
66 FROM_HERE, 86 FROM_HERE,
67 base::Bind(&FakeRemoter::Stopped, base::Unretained(this), reason)); 87 base::Bind(&FakeRemoter::Stopped, weak_factory_.GetWeakPtr(), reason));
68 } 88 }
69 89
70 void SendMessageToSink(const std::vector<uint8_t>& message) override {} 90 void SendMessageToSink(const std::vector<uint8_t>& message) override {}
71 91
72 private: 92 private:
73 void Started() { source_->OnStarted(); } 93 void Started() { source_->OnStarted(); }
74 void StartFailed() { 94 void StartFailed() {
75 source_->OnStartFailed(mojom::RemotingStartFailReason::ROUTE_TERMINATED); 95 source_->OnStartFailed(mojom::RemotingStartFailReason::ROUTE_TERMINATED);
76 } 96 }
77 void Stopped(mojom::RemotingStopReason reason) { source_->OnStopped(reason); } 97 void Stopped(mojom::RemotingStopReason reason) { source_->OnStopped(reason); }
78 98
79 mojom::RemotingSourcePtr source_; 99 const mojom::RemotingSourcePtr source_;
80 bool start_will_fail_; 100 bool start_will_fail_;
81 101
102 base::WeakPtrFactory<FakeRemoter> weak_factory_;
103
82 DISALLOW_COPY_AND_ASSIGN(FakeRemoter); 104 DISALLOW_COPY_AND_ASSIGN(FakeRemoter);
83 }; 105 };
84 106
85 class FakeRemoterFactory final : public mojom::RemoterFactory { 107 class FakeRemoterFactory final : public mojom::RemoterFactory {
86 public: 108 public:
87 // |start_will_fail| indicates whether starting remoting will fail. 109 // |start_will_fail| indicates whether starting remoting will fail.
88 explicit FakeRemoterFactory(bool start_will_fail) 110 explicit FakeRemoterFactory(bool start_will_fail)
89 : start_will_fail_(start_will_fail) {} 111 : start_will_fail_(start_will_fail) {}
90 ~FakeRemoterFactory() override {} 112 ~FakeRemoterFactory() override {}
91 113
92 void Create(mojom::RemotingSourcePtr source, 114 void Create(mojom::RemotingSourcePtr source,
93 mojom::RemoterRequest request) override { 115 mojom::RemoterRequest request) override {
94 mojo::MakeStrongBinding( 116 mojo::MakeStrongBinding(
95 base::MakeUnique<FakeRemoter>(std::move(source), start_will_fail_), 117 base::MakeUnique<FakeRemoter>(std::move(source), start_will_fail_),
96 std::move(request)); 118 std::move(request));
97 } 119 }
98 120
99 private: 121 private:
100 bool start_will_fail_; 122 bool start_will_fail_;
101 123
102 DISALLOW_COPY_AND_ASSIGN(FakeRemoterFactory); 124 DISALLOW_COPY_AND_ASSIGN(FakeRemoterFactory);
103 }; 125 };
104 126
105 std::unique_ptr<RemotingController> CreateRemotingController( 127 scoped_refptr<RemotingSourceImpl> CreateRemotingSourceImpl(
106 bool start_will_fail) { 128 bool start_will_fail) {
107 mojom::RemotingSourcePtr remoting_source; 129 mojom::RemotingSourcePtr remoting_source;
108 mojom::RemotingSourceRequest remoting_source_request = 130 mojom::RemotingSourceRequest remoting_source_request =
109 mojo::GetProxy(&remoting_source); 131 mojo::GetProxy(&remoting_source);
110 mojom::RemoterPtr remoter; 132 mojom::RemoterPtr remoter;
111 std::unique_ptr<mojom::RemoterFactory> remoter_factory = 133 std::unique_ptr<mojom::RemoterFactory> remoter_factory =
112 base::MakeUnique<FakeRemoterFactory>(start_will_fail); 134 base::MakeUnique<FakeRemoterFactory>(start_will_fail);
113 remoter_factory->Create(std::move(remoting_source), mojo::GetProxy(&remoter)); 135 remoter_factory->Create(std::move(remoting_source), mojo::GetProxy(&remoter));
114 std::unique_ptr<RemotingController> remoting_controller = 136 return new RemotingSourceImpl(std::move(remoting_source_request),
115 base::MakeUnique<RemotingController>(std::move(remoting_source_request), 137 std::move(remoter));
116 std::move(remoter));
117 return remoting_controller;
118 } 138 }
119 139
120 } // namespace 140 } // namespace
121 141
122 class RemotingControllerTest : public ::testing::Test { 142 class RemotingRendererControllerTest : public ::testing::Test {
123 public: 143 public:
124 RemotingControllerTest() 144 RemotingRendererControllerTest() {}
125 : remoting_controller_(CreateRemotingController(false)), 145 ~RemotingRendererControllerTest() override {}
126 is_remoting_(false) { 146
127 remoting_controller_->SetSwitchRendererCallback(base::Bind( 147 void TearDown() final { RunUntilIdle(); }
128 &RemotingControllerTest::ToggleRenderer, base::Unretained(this))); 148
149 static void RunUntilIdle() { base::RunLoop().RunUntilIdle(); }
150
151 void ToggleRenderer() {
152 is_rendering_remotely =
153 remoting_renderer_controller_->IsRenderingRemotely();
154 is_terminated_ = remoting_renderer_controller_->IsTerminated();
129 } 155 }
130 ~RemotingControllerTest() override {} 156
131 157 void CreateCdm(bool is_remoting) { is_remoting_cdm_ = is_remoting; }
132 void TearDown() final { RunUntilIdle(); }
133
134 static void RunUntilIdle() { base::RunLoop().RunUntilIdle(); }
135
136 void ToggleRenderer() { is_remoting_ = remoting_controller_->is_remoting(); }
137 158
138 base::MessageLoop message_loop_; 159 base::MessageLoop message_loop_;
139 160
140 protected: 161 protected:
141 std::unique_ptr<RemotingController> remoting_controller_; 162 std::unique_ptr<RemotingRendererController> remoting_renderer_controller_;
142 bool is_remoting_; 163 bool is_rendering_remotely = false;
164 bool is_remoting_cdm_ = false;
165 bool is_terminated_ = false;
143 166
144 private: 167 private:
145 DISALLOW_COPY_AND_ASSIGN(RemotingControllerTest); 168 DISALLOW_COPY_AND_ASSIGN(RemotingRendererControllerTest);
146 }; 169 };
147 170
148 TEST_F(RemotingControllerTest, ToggleRenderer) { 171 TEST_F(RemotingRendererControllerTest, ToggleRenderer) {
149 EXPECT_FALSE(is_remoting_); 172 EXPECT_FALSE(is_rendering_remotely);
150 remoting_controller_->OnSinkAvailable(); 173 scoped_refptr<RemotingSourceImpl> remoting_source_impl =
151 remoting_controller_->OnEnteredFullscreen(); 174 CreateRemotingSourceImpl(false);
152 EXPECT_FALSE(is_remoting_); 175 remoting_renderer_controller_ =
153 remoting_controller_->OnMetadataChanged(defaultMetadata()); 176 base::MakeUnique<RemotingRendererController>(remoting_source_impl);
154 RunUntilIdle(); 177 remoting_renderer_controller_->SetSwitchRendererCallback(base::Bind(
155 EXPECT_TRUE(is_remoting_); 178 &RemotingRendererControllerTest::ToggleRenderer, base::Unretained(this)));
156 remoting_controller_->OnExitedFullscreen(); 179 RunUntilIdle();
157 RunUntilIdle(); 180 EXPECT_FALSE(is_rendering_remotely);
158 EXPECT_FALSE(is_remoting_); 181 remoting_source_impl->OnSinkAvailable();
159 } 182 RunUntilIdle();
160 183 EXPECT_FALSE(is_rendering_remotely);
161 TEST_F(RemotingControllerTest, StartFailed) { 184 remoting_renderer_controller_->OnEnteredFullscreen();
162 EXPECT_FALSE(is_remoting_); 185 RunUntilIdle();
163 remoting_controller_ = CreateRemotingController(true); 186 EXPECT_FALSE(is_rendering_remotely);
164 remoting_controller_->SetSwitchRendererCallback(base::Bind( 187 remoting_renderer_controller_->OnMetadataChanged(DefaultMetadata());
165 &RemotingControllerTest::ToggleRenderer, base::Unretained(this))); 188 RunUntilIdle();
166 remoting_controller_->OnSinkAvailable(); 189 EXPECT_TRUE(is_rendering_remotely);
167 remoting_controller_->OnEnteredFullscreen(); 190 remoting_renderer_controller_->OnExitedFullscreen();
168 remoting_controller_->OnMetadataChanged(defaultMetadata()); 191 RunUntilIdle();
169 RunUntilIdle(); 192 EXPECT_FALSE(is_rendering_remotely);
170 EXPECT_FALSE(is_remoting_); 193 }
194
195 TEST_F(RemotingRendererControllerTest, StartFailed) {
196 EXPECT_FALSE(is_rendering_remotely);
197 scoped_refptr<RemotingSourceImpl> remoting_source_impl =
198 CreateRemotingSourceImpl(true);
199 remoting_renderer_controller_ =
200 base::MakeUnique<RemotingRendererController>(remoting_source_impl);
201 remoting_renderer_controller_->SetSwitchRendererCallback(base::Bind(
202 &RemotingRendererControllerTest::ToggleRenderer, base::Unretained(this)));
203 RunUntilIdle();
204 EXPECT_FALSE(is_rendering_remotely);
205 remoting_source_impl->OnSinkAvailable();
206 RunUntilIdle();
207 EXPECT_FALSE(is_rendering_remotely);
208 remoting_renderer_controller_->OnEnteredFullscreen();
209 RunUntilIdle();
210 EXPECT_FALSE(is_rendering_remotely);
211 remoting_renderer_controller_->OnMetadataChanged(DefaultMetadata());
212 RunUntilIdle();
213 EXPECT_FALSE(is_rendering_remotely);
214 }
215
216 TEST_F(RemotingRendererControllerTest, EncryptedWithRemotingCdm) {
217 EXPECT_FALSE(is_rendering_remotely);
218 remoting_renderer_controller_ = base::MakeUnique<RemotingRendererController>(
219 CreateRemotingSourceImpl(false));
220 remoting_renderer_controller_->SetSwitchRendererCallback(base::Bind(
221 &RemotingRendererControllerTest::ToggleRenderer, base::Unretained(this)));
222 RunUntilIdle();
223 EXPECT_FALSE(is_rendering_remotely);
224 remoting_renderer_controller_->OnMetadataChanged(EncryptedMetadata());
225 RunUntilIdle();
226 EXPECT_FALSE(is_rendering_remotely);
227 scoped_refptr<RemotingSourceImpl> cdm_remoting_source_impl =
228 CreateRemotingSourceImpl(false);
229 std::unique_ptr<RemotingCdmController> remoting_cdm_controller =
230 base::MakeUnique<RemotingCdmController>(cdm_remoting_source_impl);
231 cdm_remoting_source_impl->OnSinkAvailable();
232 remoting_cdm_controller->ShouldCreateRemotingCdm(base::Bind(
233 &RemotingRendererControllerTest::CreateCdm, base::Unretained(this)));
234 RunUntilIdle();
235 EXPECT_FALSE(is_rendering_remotely);
236 EXPECT_TRUE(is_remoting_cdm_);
237
238 CdmConfig cdm_config;
239 GURL gurl;
240 std::string empty_string;
241 scoped_refptr<RemotingCdm> remoting_cdm = new RemotingCdm(
242 empty_string, gurl, cdm_config, SessionMessageCB(), SessionClosedCB(),
243 SessionKeysChangeCB(), SessionExpirationUpdateCB(), CdmCreatedCB(),
244 std::move(remoting_cdm_controller));
245 remoting_renderer_controller_->OnSetCdm(remoting_cdm.get());
246 RunUntilIdle();
247 EXPECT_TRUE(is_rendering_remotely);
248
249 // For encrypted contents, entering/exiting full screen has no effect.
250 remoting_renderer_controller_->OnEnteredFullscreen();
251 RunUntilIdle();
252 EXPECT_TRUE(is_rendering_remotely);
253 remoting_renderer_controller_->OnExitedFullscreen();
254 RunUntilIdle();
255 EXPECT_TRUE(is_rendering_remotely);
256
257 EXPECT_FALSE(is_terminated_);
258 cdm_remoting_source_impl->OnSinkGone();
259 RunUntilIdle();
260 EXPECT_TRUE(is_terminated_);
261 EXPECT_FALSE(is_rendering_remotely);
262 }
263
264 TEST_F(RemotingRendererControllerTest, EncryptedWithLocalCdm) {
265 EXPECT_FALSE(is_rendering_remotely);
266 scoped_refptr<RemotingSourceImpl> renderer_remoting_source_impl =
267 CreateRemotingSourceImpl(false);
268 remoting_renderer_controller_ = base::MakeUnique<RemotingRendererController>(
269 renderer_remoting_source_impl);
270 remoting_renderer_controller_->SetSwitchRendererCallback(base::Bind(
271 &RemotingRendererControllerTest::ToggleRenderer, base::Unretained(this)));
272 RunUntilIdle();
273 EXPECT_FALSE(is_rendering_remotely);
274 renderer_remoting_source_impl->OnSinkAvailable();
275 RunUntilIdle();
276 EXPECT_FALSE(is_rendering_remotely);
277 remoting_renderer_controller_->OnEnteredFullscreen();
278 RunUntilIdle();
279 EXPECT_FALSE(is_rendering_remotely);
280 remoting_renderer_controller_->OnMetadataChanged(EncryptedMetadata());
281 RunUntilIdle();
282 EXPECT_FALSE(is_rendering_remotely);
283
284 scoped_refptr<RemotingSourceImpl> cdm_remoting_source_impl =
285 CreateRemotingSourceImpl(true);
286 std::unique_ptr<RemotingCdmController> remoting_cdm_controller =
287 base::MakeUnique<RemotingCdmController>(cdm_remoting_source_impl);
288 cdm_remoting_source_impl->OnSinkAvailable();
289 remoting_cdm_controller->ShouldCreateRemotingCdm(base::Bind(
290 &RemotingRendererControllerTest::CreateCdm, base::Unretained(this)));
291 RunUntilIdle();
292 EXPECT_FALSE(is_rendering_remotely);
293 EXPECT_FALSE(is_remoting_cdm_);
294
295 CdmConfig cdm_config;
296 GURL gurl;
297 std::string empty_string;
298 scoped_refptr<RemotingCdm> remoting_cdm = new RemotingCdm(
299 empty_string, gurl, cdm_config, SessionMessageCB(), SessionClosedCB(),
300 SessionKeysChangeCB(), SessionExpirationUpdateCB(), CdmCreatedCB(),
301 std::move(remoting_cdm_controller));
302 remoting_renderer_controller_->OnSetCdm(remoting_cdm.get());
303 RunUntilIdle();
304 EXPECT_FALSE(is_rendering_remotely);
305 EXPECT_FALSE(is_terminated_);
306 }
307
308 TEST_F(RemotingRendererControllerTest, EncryptedWithFailedRemotingCdm) {
309 EXPECT_FALSE(is_rendering_remotely);
310 remoting_renderer_controller_ = base::MakeUnique<RemotingRendererController>(
311 CreateRemotingSourceImpl(false));
312 remoting_renderer_controller_->SetSwitchRendererCallback(base::Bind(
313 &RemotingRendererControllerTest::ToggleRenderer, base::Unretained(this)));
314 RunUntilIdle();
315 EXPECT_FALSE(is_rendering_remotely);
316 remoting_renderer_controller_->OnEnteredFullscreen();
317 RunUntilIdle();
318 EXPECT_FALSE(is_rendering_remotely);
319 remoting_renderer_controller_->OnMetadataChanged(EncryptedMetadata());
320 RunUntilIdle();
321 EXPECT_FALSE(is_rendering_remotely);
322
323 scoped_refptr<RemotingSourceImpl> cdm_remoting_source_impl =
324 CreateRemotingSourceImpl(false);
325 std::unique_ptr<RemotingCdmController> remoting_cdm_controller =
326 base::MakeUnique<RemotingCdmController>(cdm_remoting_source_impl);
327 cdm_remoting_source_impl->OnSinkAvailable();
328 remoting_cdm_controller->ShouldCreateRemotingCdm(base::Bind(
329 &RemotingRendererControllerTest::CreateCdm, base::Unretained(this)));
330 RunUntilIdle();
331 EXPECT_FALSE(is_rendering_remotely);
332 EXPECT_TRUE(is_remoting_cdm_);
333
334 cdm_remoting_source_impl->OnSinkGone();
335 RunUntilIdle();
336 EXPECT_FALSE(is_rendering_remotely);
337 EXPECT_FALSE(is_terminated_);
338
339 CdmConfig cdm_config;
340 GURL gurl;
341 std::string empty_string;
342 scoped_refptr<RemotingCdm> remoting_cdm = new RemotingCdm(
343 empty_string, gurl, cdm_config, SessionMessageCB(), SessionClosedCB(),
344 SessionKeysChangeCB(), SessionExpirationUpdateCB(), CdmCreatedCB(),
345 std::move(remoting_cdm_controller));
346 remoting_renderer_controller_->OnSetCdm(remoting_cdm.get());
347 RunUntilIdle();
348 EXPECT_FALSE(is_rendering_remotely);
349 EXPECT_TRUE(is_terminated_);
171 } 350 }
172 351
173 } // namespace media 352 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698