OLD | NEW |
---|---|
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 "extensions/renderer/api/display_source/wifi_display/wifi_display_media _manager.h" | 5 #include "extensions/renderer/api/display_source/wifi_display/wifi_display_media _manager.h" |
6 | 6 |
7 #include "base/logging.h" | 7 #include "base/logging.h" |
8 #include "base/rand_util.h" | 8 #include "base/rand_util.h" |
9 #include "base/task_runner_util.h" | |
9 #include "content/public/renderer/media_stream_utils.h" | 10 #include "content/public/renderer/media_stream_utils.h" |
11 #include "content/public/renderer/media_stream_video_sink.h" | |
12 #include "content/public/renderer/render_thread.h" | |
13 #include "content/public/renderer/video_encode_accelerator.h" | |
14 #include "extensions/common/mojo/wifi_display_session_service.mojom.h" | |
15 #include "extensions/renderer/api/display_source/wifi_display/wifi_display_eleme ntary_stream_info.h" | |
16 #include "extensions/renderer/api/display_source/wifi_display/wifi_display_media _pipeline.h" | |
17 #include "media/base/bind_to_current_loop.h" | |
10 | 18 |
11 namespace extensions { | 19 namespace extensions { |
12 | 20 |
13 namespace { | 21 namespace { |
14 | 22 |
15 const char kErrorNoVideoFormatData[] = | 23 const char kErrorNoVideoFormatData[] = |
16 "Failed to get video format data from the given MediaStreamTrack object"; | 24 "Failed to get video format data from the given MediaStreamTrack object"; |
17 const char kErrorSinkCannotPlayVideo[] = | 25 const char kErrorSinkCannotPlayVideo[] = |
18 "The sink cannot play video from the given MediaStreamTrack object"; | 26 "The sink cannot play video from the given MediaStreamTrack object"; |
19 const char kErrorSinkCannotPlayAudio[] = | 27 const char kErrorSinkCannotPlayAudio[] = |
20 "The sink cannot play audio from the given MediaStreamTrack object"; | 28 "The sink cannot play audio from the given MediaStreamTrack object"; |
29 const char kErrorMediaPipelineFailure[] = | |
30 "Failed to initialize media pipeline for the session"; | |
21 } // namespace | 31 } // namespace |
22 | 32 |
33 class WiFiDisplayVideoSink : public content::MediaStreamVideoSink { | |
34 public: | |
35 WiFiDisplayVideoSink( | |
36 const blink::WebMediaStreamTrack& track, | |
37 const content::VideoCaptureDeliverFrameCB& callback) | |
38 : track_(track), | |
39 sink_added_(false), | |
40 callback_(callback) { | |
41 } | |
42 | |
43 ~WiFiDisplayVideoSink() override { | |
44 Stop(); | |
45 } | |
46 | |
47 void Start() { | |
48 DCHECK(!sink_added_); | |
49 sink_added_ = true; | |
50 // Callback is invoked on IO thread. | |
51 AddToVideoTrack(this, callback_, track_); | |
52 } | |
53 | |
54 void Stop() { | |
55 if (sink_added_) | |
56 RemoveFromVideoTrack(this, track_); | |
57 } | |
58 | |
59 private: | |
60 blink::WebMediaStreamTrack track_; | |
61 bool sink_added_; | |
62 content::VideoCaptureDeliverFrameCB callback_; | |
63 DISALLOW_COPY_AND_ASSIGN(WiFiDisplayVideoSink); | |
64 }; | |
65 | |
23 WiFiDisplayMediaManager::WiFiDisplayMediaManager( | 66 WiFiDisplayMediaManager::WiFiDisplayMediaManager( |
24 const blink::WebMediaStreamTrack& video_track, | 67 const blink::WebMediaStreamTrack& video_track, |
25 const blink::WebMediaStreamTrack& audio_track, | 68 const blink::WebMediaStreamTrack& audio_track, |
26 const ErrorCallback& error_callback) | 69 const ErrorCallback& error_callback) |
27 : video_track_(video_track), | 70 : video_track_(video_track), |
28 audio_track_(audio_track), | 71 audio_track_(audio_track), |
29 error_callback_(error_callback) { | 72 player_(nullptr), |
73 io_task_runner_(content::RenderThread::Get()->GetIOMessageLoopProxy()), | |
74 error_callback_(error_callback), | |
75 is_playing_(false), | |
76 is_initialized_(false), | |
77 weak_factory_(this) { | |
30 DCHECK(!video_track.isNull() || !audio_track.isNull()); | 78 DCHECK(!video_track.isNull() || !audio_track.isNull()); |
31 DCHECK(!error_callback_.is_null()); | 79 DCHECK(!error_callback_.is_null()); |
32 } | 80 } |
33 | 81 |
34 WiFiDisplayMediaManager::~WiFiDisplayMediaManager() { | 82 WiFiDisplayMediaManager::~WiFiDisplayMediaManager() { |
83 Teardown(); | |
35 } | 84 } |
36 | 85 |
37 void WiFiDisplayMediaManager::Play() { | 86 void WiFiDisplayMediaManager::Play() { |
38 NOTIMPLEMENTED(); | 87 is_playing_ = true; |
88 if (!player_) { | |
89 base::PostTaskAndReplyWithResult(io_task_runner_.get(), FROM_HERE, | |
90 base::Bind( | |
91 &WiFiDisplayMediaPipeline::Create, | |
92 GetSessionType(), | |
93 video_encoder_parameters_, | |
94 optimal_audio_codec_, | |
95 media::BindToCurrentLoop(error_callback_)), | |
96 base::Bind(&WiFiDisplayMediaManager::OnPlayerCreated, | |
97 weak_factory_.GetWeakPtr())); | |
98 return; | |
99 } | |
100 | |
101 if (!is_initialized_) { | |
102 return; // Waiting for initialization being completed. | |
103 } | |
104 | |
105 if (!video_track_.isNull()) { | |
106 // To be called on IO thread. | |
107 auto on_raw_video_frame = base::Bind( | |
108 &WiFiDisplayMediaPipeline::InsertRawVideoFrame, | |
109 base::Unretained(player_)); | |
110 video_sink_.reset( | |
111 new WiFiDisplayVideoSink(video_track_, on_raw_video_frame)); | |
112 video_sink_->Start(); | |
113 LOG(ERROR) << "video sink started"; | |
114 } | |
39 } | 115 } |
40 | 116 |
41 void WiFiDisplayMediaManager::Teardown() { | 117 void WiFiDisplayMediaManager::Teardown() { |
42 NOTIMPLEMENTED(); | 118 Pause(); |
119 if (player_) { | |
120 io_task_runner_->DeleteSoon(FROM_HERE, player_); | |
121 player_ = nullptr; | |
122 } | |
123 is_initialized_ = false; | |
43 } | 124 } |
44 | 125 |
45 void WiFiDisplayMediaManager::Pause() { | 126 void WiFiDisplayMediaManager::Pause() { |
46 NOTIMPLEMENTED(); | 127 is_playing_ = false; |
128 video_sink_.reset(); | |
47 } | 129 } |
48 | 130 |
49 bool WiFiDisplayMediaManager::IsPaused() const { | 131 bool WiFiDisplayMediaManager::IsPaused() const { |
50 NOTIMPLEMENTED(); | 132 return !is_playing_; |
51 return true; | |
52 } | 133 } |
53 | 134 |
54 wds::SessionType WiFiDisplayMediaManager::GetSessionType() const { | 135 wds::SessionType WiFiDisplayMediaManager::GetSessionType() const { |
55 uint16_t session_type = 0; | 136 uint16_t session_type = 0; |
56 if (!video_track_.isNull()) | 137 if (!video_track_.isNull()) |
57 session_type |= wds::VideoSession; | 138 session_type |= wds::VideoSession; |
58 | 139 |
59 if (!audio_track_.isNull()) | 140 if (!audio_track_.isNull()) |
60 session_type |= wds::AudioSession; | 141 session_type |= wds::AudioSession; |
61 | 142 |
(...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
186 } | 267 } |
187 return false; | 268 return false; |
188 } | 269 } |
189 | 270 |
190 } // namespace | 271 } // namespace |
191 | 272 |
192 wds::H264VideoFormat WiFiDisplayMediaManager::GetOptimalVideoFormat() const { | 273 wds::H264VideoFormat WiFiDisplayMediaManager::GetOptimalVideoFormat() const { |
193 return optimal_video_format_; | 274 return optimal_video_format_; |
194 } | 275 } |
195 | 276 |
196 void WiFiDisplayMediaManager::SendIDRPicture() { | 277 namespace { |
197 NOTIMPLEMENTED(); | 278 |
279 int GetBitRate(const gfx::Size& frame_size) { | |
280 DCHECK_GE(frame_size.height(), 360); | |
281 if (frame_size.height() < 720) | |
282 return 2500000; | |
283 if (frame_size.height() < 1080) | |
284 return 5000000; | |
285 return 8000000; | |
198 } | 286 } |
199 | 287 |
200 std::string WiFiDisplayMediaManager::GetSessionId() const { | 288 void CreateVideoEncodeMemory( |
201 return base::RandBytesAsString(8); | 289 size_t size, |
290 const WiFiDisplayVideoEncoder::ReceiveEncodeMemoryCallback& callback) { | |
291 DCHECK(content::RenderThread::Get()); | |
292 | |
293 scoped_ptr<base::SharedMemory> shm = | |
294 content::RenderThread::Get()->HostAllocateSharedMemoryBuffer(size); | |
295 DCHECK(shm) << "Failed to allocate shared memory"; | |
296 if (!shm->Map(size)) { | |
297 NOTREACHED() << "Map failed"; | |
298 } | |
299 callback.Run(std::move(shm)); | |
202 } | 300 } |
203 | 301 |
302 } // namespace | |
303 | |
204 bool WiFiDisplayMediaManager::InitOptimalVideoFormat( | 304 bool WiFiDisplayMediaManager::InitOptimalVideoFormat( |
205 const wds::NativeVideoFormat& sink_native_format, | 305 const wds::NativeVideoFormat& sink_native_format, |
206 const std::vector<wds::H264VideoCodec>& sink_supported_codecs) { | 306 const std::vector<wds::H264VideoCodec>& sink_supported_codecs) { |
207 const media::VideoCaptureFormat* capture_format = | 307 const media::VideoCaptureFormat* capture_format = |
208 content::GetCurrentVideoTrackFormat(video_track_); | 308 content::GetCurrentVideoTrackFormat(video_track_); |
209 if (!capture_format) { | 309 if (!capture_format) { |
210 error_callback_.Run(kErrorNoVideoFormatData); | 310 error_callback_.Run(kErrorNoVideoFormatData); |
211 return false; | 311 return false; |
212 } | 312 } |
213 | 313 |
214 if (!FindOptimalFormat( | 314 if (!FindOptimalFormat( |
215 capture_format, sink_supported_codecs, &optimal_video_format_)) { | 315 capture_format, sink_supported_codecs, &optimal_video_format_)) { |
216 error_callback_.Run(kErrorSinkCannotPlayVideo); | 316 error_callback_.Run(kErrorSinkCannotPlayVideo); |
217 return false; | 317 return false; |
218 } | 318 } |
319 video_encoder_parameters_.frame_size = capture_format->frame_size; | |
320 video_encoder_parameters_.frame_rate = | |
321 static_cast<int>(capture_format->frame_rate); | |
322 video_encoder_parameters_.bit_rate = GetBitRate(capture_format->frame_size); | |
323 video_encoder_parameters_.profile = optimal_video_format_.profile; | |
324 video_encoder_parameters_.level = optimal_video_format_.level; | |
325 video_encoder_parameters_.create_memory_callback = | |
326 media::BindToCurrentLoop(base::Bind(&CreateVideoEncodeMemory)); | |
327 video_encoder_parameters_.vea_create_callback = | |
328 media::BindToCurrentLoop( | |
329 base::Bind(&content::CreateVideoEncodeAccelerator)); | |
219 | 330 |
220 return true; | 331 return true; |
221 } | 332 } |
222 | 333 |
223 bool WiFiDisplayMediaManager::InitOptimalAudioFormat( | 334 bool WiFiDisplayMediaManager::InitOptimalAudioFormat( |
224 const std::vector<wds::AudioCodec>& sink_codecs) { | 335 const std::vector<wds::AudioCodec>& sink_codecs) { |
225 for (const wds::AudioCodec& codec : sink_codecs) { | 336 for (const wds::AudioCodec& codec : sink_codecs) { |
226 // MediaStreamTrack contains LPCM audio. | 337 // MediaStreamTrack contains LPCM audio. |
227 if (codec.format == wds::LPCM) { | 338 if (codec.format == wds::LPCM) { |
228 optimal_audio_codec_ = codec; | 339 optimal_audio_codec_ = codec; |
229 // Picking a single mode. | 340 // Picking a single mode. |
230 wds::AudioModes optimal_mode; | 341 wds::AudioModes optimal_mode; |
231 if (codec.modes.test(wds::LPCM_44_1K_16B_2CH)) | 342 if (codec.modes.test(wds::LPCM_44_1K_16B_2CH)) |
232 optimal_mode.set(wds::LPCM_44_1K_16B_2CH); | 343 optimal_mode.set(wds::LPCM_44_1K_16B_2CH); |
233 else | 344 else |
234 optimal_mode.set(wds::LPCM_48K_16B_2CH); | 345 optimal_mode.set(wds::LPCM_48K_16B_2CH); |
235 optimal_audio_codec_.modes = optimal_mode; | 346 optimal_audio_codec_.modes = optimal_mode; |
236 return true; | 347 return true; |
237 } | 348 } |
238 } | 349 } |
239 error_callback_.Run(kErrorSinkCannotPlayAudio); | 350 error_callback_.Run(kErrorSinkCannotPlayAudio); |
240 return false; | 351 return false; |
241 } | 352 } |
242 | 353 |
243 wds::AudioCodec WiFiDisplayMediaManager::GetOptimalAudioFormat() const { | 354 wds::AudioCodec WiFiDisplayMediaManager::GetOptimalAudioFormat() const { |
244 return optimal_audio_codec_; | 355 return optimal_audio_codec_; |
245 } | 356 } |
246 | 357 |
358 void WiFiDisplayMediaManager::SendIDRPicture() { | |
359 DCHECK(player_); | |
360 io_task_runner_->PostTask(FROM_HERE, | |
361 base::Bind(&WiFiDisplayMediaPipeline::RequestIDRPicture, | |
362 base::Unretained(player_))); | |
363 } | |
364 | |
365 std::string WiFiDisplayMediaManager::GetSessionId() const { | |
366 return base::RandBytesAsString(8); | |
shalamov
2016/04/06 07:48:48
I think session id should be unique during the ses
Mikhail
2016/04/06 09:40:59
Fixed, ptal
| |
367 } | |
368 | |
369 void WiFiDisplayMediaManager::OnPlayerCreated( | |
370 scoped_ptr<WiFiDisplayMediaPipeline> player) { | |
371 DCHECK(player); | |
372 DCHECK(content::RenderThread::Get()); | |
373 player_ = player.release(); | |
374 | |
375 auto completion_callback = base::Bind( | |
376 &WiFiDisplayMediaManager::OnMediaPipelineInitialized, | |
377 weak_factory_.GetWeakPtr()); | |
378 | |
379 io_task_runner_->PostTask(FROM_HERE, | |
380 base::Bind(&WiFiDisplayMediaPipeline::Initialize, | |
381 base::Unretained(player_), | |
382 media::BindToCurrentLoop(completion_callback))); | |
383 } | |
384 | |
385 void WiFiDisplayMediaManager::OnMediaPipelineInitialized(bool success) { | |
386 DCHECK(content::RenderThread::Get()); | |
387 is_initialized_ = success; | |
388 | |
389 if (!is_initialized_) { | |
390 error_callback_.Run(kErrorMediaPipelineFailure); | |
391 return; | |
392 } | |
393 | |
394 if (is_playing_) | |
395 Play(); | |
396 } | |
397 | |
398 | |
247 } // namespace extensions | 399 } // namespace extensions |
OLD | NEW |