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 } | |
39 } | 114 } |
40 | 115 |
41 void WiFiDisplayMediaManager::Teardown() { | 116 void WiFiDisplayMediaManager::Teardown() { |
42 NOTIMPLEMENTED(); | 117 Pause(); |
118 if (player_) { | |
119 io_task_runner_->DeleteSoon(FROM_HERE, player_); | |
120 player_ = nullptr; | |
121 } | |
122 is_initialized_ = false; | |
123 session_id_.clear(); | |
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 std::unique_ptr<base::SharedMemory> shm = | |
294 content::RenderThread::Get()->HostAllocateSharedMemoryBuffer(size); | |
295 if (!shm || !shm->Map(size)) { | |
296 NOTREACHED() << "Shared memory allocation or map failed"; | |
297 } | |
298 callback.Run(std::move(shm)); | |
202 } | 299 } |
203 | 300 |
301 } // namespace | |
302 | |
204 bool WiFiDisplayMediaManager::InitOptimalVideoFormat( | 303 bool WiFiDisplayMediaManager::InitOptimalVideoFormat( |
205 const wds::NativeVideoFormat& sink_native_format, | 304 const wds::NativeVideoFormat& sink_native_format, |
206 const std::vector<wds::H264VideoCodec>& sink_supported_codecs) { | 305 const std::vector<wds::H264VideoCodec>& sink_supported_codecs) { |
207 const media::VideoCaptureFormat* capture_format = | 306 const media::VideoCaptureFormat* capture_format = |
208 content::GetCurrentVideoTrackFormat(video_track_); | 307 content::GetCurrentVideoTrackFormat(video_track_); |
209 if (!capture_format) { | 308 if (!capture_format) { |
210 error_callback_.Run(kErrorNoVideoFormatData); | 309 error_callback_.Run(kErrorNoVideoFormatData); |
211 return false; | 310 return false; |
212 } | 311 } |
213 | 312 |
214 if (!FindOptimalFormat( | 313 if (!FindOptimalFormat( |
215 capture_format, sink_supported_codecs, &optimal_video_format_)) { | 314 capture_format, sink_supported_codecs, &optimal_video_format_)) { |
216 error_callback_.Run(kErrorSinkCannotPlayVideo); | 315 error_callback_.Run(kErrorSinkCannotPlayVideo); |
217 return false; | 316 return false; |
218 } | 317 } |
318 video_encoder_parameters_.frame_size = capture_format->frame_size; | |
319 video_encoder_parameters_.frame_rate = | |
320 static_cast<int>(capture_format->frame_rate); | |
321 video_encoder_parameters_.bit_rate = GetBitRate(capture_format->frame_size); | |
322 video_encoder_parameters_.profile = optimal_video_format_.profile; | |
323 video_encoder_parameters_.level = optimal_video_format_.level; | |
324 video_encoder_parameters_.create_memory_callback = | |
325 media::BindToCurrentLoop(base::Bind(&CreateVideoEncodeMemory)); | |
ddorwin
2016/04/08 17:41:31
Why is media::BindToCurrentLoop being used outside
Mikhail
2016/04/08 18:28:58
IMO this is convenient and quite generic feature w
| |
326 video_encoder_parameters_.vea_create_callback = | |
ddorwin
2016/04/08 17:41:31
vea_create_callback is not used anywhere.
VEA has
Mikhail
2016/04/08 18:28:58
This functionality is in the coming patches we'll
| |
327 media::BindToCurrentLoop( | |
328 base::Bind(&content::CreateVideoEncodeAccelerator)); | |
219 | 329 |
220 return true; | 330 return true; |
221 } | 331 } |
222 | 332 |
223 bool WiFiDisplayMediaManager::InitOptimalAudioFormat( | 333 bool WiFiDisplayMediaManager::InitOptimalAudioFormat( |
224 const std::vector<wds::AudioCodec>& sink_codecs) { | 334 const std::vector<wds::AudioCodec>& sink_codecs) { |
225 for (const wds::AudioCodec& codec : sink_codecs) { | 335 for (const wds::AudioCodec& codec : sink_codecs) { |
226 // MediaStreamTrack contains LPCM audio. | 336 // MediaStreamTrack contains LPCM audio. |
227 if (codec.format == wds::LPCM) { | 337 if (codec.format == wds::LPCM) { |
228 optimal_audio_codec_ = codec; | 338 optimal_audio_codec_ = codec; |
229 // Picking a single mode. | 339 // Picking a single mode. |
230 wds::AudioModes optimal_mode; | 340 wds::AudioModes optimal_mode; |
231 if (codec.modes.test(wds::LPCM_44_1K_16B_2CH)) | 341 if (codec.modes.test(wds::LPCM_44_1K_16B_2CH)) |
232 optimal_mode.set(wds::LPCM_44_1K_16B_2CH); | 342 optimal_mode.set(wds::LPCM_44_1K_16B_2CH); |
233 else | 343 else |
234 optimal_mode.set(wds::LPCM_48K_16B_2CH); | 344 optimal_mode.set(wds::LPCM_48K_16B_2CH); |
235 optimal_audio_codec_.modes = optimal_mode; | 345 optimal_audio_codec_.modes = optimal_mode; |
236 return true; | 346 return true; |
237 } | 347 } |
238 } | 348 } |
239 error_callback_.Run(kErrorSinkCannotPlayAudio); | 349 error_callback_.Run(kErrorSinkCannotPlayAudio); |
240 return false; | 350 return false; |
241 } | 351 } |
242 | 352 |
243 wds::AudioCodec WiFiDisplayMediaManager::GetOptimalAudioFormat() const { | 353 wds::AudioCodec WiFiDisplayMediaManager::GetOptimalAudioFormat() const { |
244 return optimal_audio_codec_; | 354 return optimal_audio_codec_; |
245 } | 355 } |
246 | 356 |
357 void WiFiDisplayMediaManager::SendIDRPicture() { | |
358 DCHECK(player_); | |
359 io_task_runner_->PostTask(FROM_HERE, | |
360 base::Bind(&WiFiDisplayMediaPipeline::RequestIDRPicture, | |
361 base::Unretained(player_))); | |
362 } | |
363 | |
364 std::string WiFiDisplayMediaManager::GetSessionId() const { | |
365 if (session_id_.empty()) | |
366 session_id_ = base::RandBytesAsString(8); | |
367 return session_id_; | |
368 } | |
369 | |
370 void WiFiDisplayMediaManager::OnPlayerCreated( | |
371 std::unique_ptr<WiFiDisplayMediaPipeline> player) { | |
372 DCHECK(player); | |
373 DCHECK(content::RenderThread::Get()); | |
374 player_ = player.release(); | |
375 | |
376 auto completion_callback = base::Bind( | |
377 &WiFiDisplayMediaManager::OnMediaPipelineInitialized, | |
378 weak_factory_.GetWeakPtr()); | |
379 | |
380 io_task_runner_->PostTask(FROM_HERE, | |
381 base::Bind(&WiFiDisplayMediaPipeline::Initialize, | |
382 base::Unretained(player_), | |
383 media::BindToCurrentLoop(completion_callback))); | |
384 } | |
385 | |
386 void WiFiDisplayMediaManager::OnMediaPipelineInitialized(bool success) { | |
387 DCHECK(content::RenderThread::Get()); | |
388 is_initialized_ = success; | |
389 | |
390 if (!is_initialized_) { | |
391 error_callback_.Run(kErrorMediaPipelineFailure); | |
392 return; | |
393 } | |
394 | |
395 if (is_playing_) | |
396 Play(); | |
397 } | |
398 | |
399 | |
247 } // namespace extensions | 400 } // namespace extensions |
OLD | NEW |