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)); |
| 326 video_encoder_parameters_.vea_create_callback = |
| 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 |