Chromium Code Reviews| 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; | |
| 124 session_id_.clear(); | |
| 43 } | 125 } |
| 44 | 126 |
| 45 void WiFiDisplayMediaManager::Pause() { | 127 void WiFiDisplayMediaManager::Pause() { |
| 46 NOTIMPLEMENTED(); | 128 is_playing_ = false; |
| 129 video_sink_.reset(); | |
| 47 } | 130 } |
| 48 | 131 |
| 49 bool WiFiDisplayMediaManager::IsPaused() const { | 132 bool WiFiDisplayMediaManager::IsPaused() const { |
| 50 NOTIMPLEMENTED(); | 133 return !is_playing_; |
| 51 return true; | |
| 52 } | 134 } |
| 53 | 135 |
| 54 wds::SessionType WiFiDisplayMediaManager::GetSessionType() const { | 136 wds::SessionType WiFiDisplayMediaManager::GetSessionType() const { |
| 55 uint16_t session_type = 0; | 137 uint16_t session_type = 0; |
| 56 if (!video_track_.isNull()) | 138 if (!video_track_.isNull()) |
| 57 session_type |= wds::VideoSession; | 139 session_type |= wds::VideoSession; |
| 58 | 140 |
| 59 if (!audio_track_.isNull()) | 141 if (!audio_track_.isNull()) |
| 60 session_type |= wds::AudioSession; | 142 session_type |= wds::AudioSession; |
| 61 | 143 |
| (...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 186 } | 268 } |
| 187 return false; | 269 return false; |
| 188 } | 270 } |
| 189 | 271 |
| 190 } // namespace | 272 } // namespace |
| 191 | 273 |
| 192 wds::H264VideoFormat WiFiDisplayMediaManager::GetOptimalVideoFormat() const { | 274 wds::H264VideoFormat WiFiDisplayMediaManager::GetOptimalVideoFormat() const { |
| 193 return optimal_video_format_; | 275 return optimal_video_format_; |
| 194 } | 276 } |
| 195 | 277 |
| 196 void WiFiDisplayMediaManager::SendIDRPicture() { | 278 namespace { |
| 197 NOTIMPLEMENTED(); | 279 |
| 280 int GetBitRate(const gfx::Size& frame_size) { | |
| 281 DCHECK_GE(frame_size.height(), 360); | |
| 282 if (frame_size.height() < 720) | |
| 283 return 2500000; | |
| 284 if (frame_size.height() < 1080) | |
| 285 return 5000000; | |
| 286 return 8000000; | |
| 198 } | 287 } |
| 199 | 288 |
| 200 std::string WiFiDisplayMediaManager::GetSessionId() const { | 289 void CreateVideoEncodeMemory( |
| 201 return base::RandBytesAsString(8); | 290 size_t size, |
| 291 const WiFiDisplayVideoEncoder::ReceiveEncodeMemoryCallback& callback) { | |
| 292 DCHECK(content::RenderThread::Get()); | |
| 293 | |
| 294 scoped_ptr<base::SharedMemory> shm = | |
| 295 content::RenderThread::Get()->HostAllocateSharedMemoryBuffer(size); | |
| 296 DCHECK(shm) << "Failed to allocate shared memory"; | |
|
asargent_no_longer_on_chrome
2016/04/06 20:53:21
Maybe this should just be a CHECK?
Alternately, m
Mikhail
2016/04/07 09:07:25
We can just pass nullptr to encoder that will be a
| |
| 297 if (!shm->Map(size)) { | |
| 298 NOTREACHED() << "Map failed"; | |
| 299 } | |
| 300 callback.Run(std::move(shm)); | |
| 202 } | 301 } |
| 203 | 302 |
| 303 } // namespace | |
| 304 | |
| 204 bool WiFiDisplayMediaManager::InitOptimalVideoFormat( | 305 bool WiFiDisplayMediaManager::InitOptimalVideoFormat( |
| 205 const wds::NativeVideoFormat& sink_native_format, | 306 const wds::NativeVideoFormat& sink_native_format, |
| 206 const std::vector<wds::H264VideoCodec>& sink_supported_codecs) { | 307 const std::vector<wds::H264VideoCodec>& sink_supported_codecs) { |
| 207 const media::VideoCaptureFormat* capture_format = | 308 const media::VideoCaptureFormat* capture_format = |
| 208 content::GetCurrentVideoTrackFormat(video_track_); | 309 content::GetCurrentVideoTrackFormat(video_track_); |
| 209 if (!capture_format) { | 310 if (!capture_format) { |
| 210 error_callback_.Run(kErrorNoVideoFormatData); | 311 error_callback_.Run(kErrorNoVideoFormatData); |
| 211 return false; | 312 return false; |
| 212 } | 313 } |
| 213 | 314 |
| 214 if (!FindOptimalFormat( | 315 if (!FindOptimalFormat( |
| 215 capture_format, sink_supported_codecs, &optimal_video_format_)) { | 316 capture_format, sink_supported_codecs, &optimal_video_format_)) { |
| 216 error_callback_.Run(kErrorSinkCannotPlayVideo); | 317 error_callback_.Run(kErrorSinkCannotPlayVideo); |
| 217 return false; | 318 return false; |
| 218 } | 319 } |
| 320 video_encoder_parameters_.frame_size = capture_format->frame_size; | |
| 321 video_encoder_parameters_.frame_rate = | |
| 322 static_cast<int>(capture_format->frame_rate); | |
| 323 video_encoder_parameters_.bit_rate = GetBitRate(capture_format->frame_size); | |
| 324 video_encoder_parameters_.profile = optimal_video_format_.profile; | |
| 325 video_encoder_parameters_.level = optimal_video_format_.level; | |
| 326 video_encoder_parameters_.create_memory_callback = | |
| 327 media::BindToCurrentLoop(base::Bind(&CreateVideoEncodeMemory)); | |
| 328 video_encoder_parameters_.vea_create_callback = | |
| 329 media::BindToCurrentLoop( | |
| 330 base::Bind(&content::CreateVideoEncodeAccelerator)); | |
| 219 | 331 |
| 220 return true; | 332 return true; |
| 221 } | 333 } |
| 222 | 334 |
| 223 bool WiFiDisplayMediaManager::InitOptimalAudioFormat( | 335 bool WiFiDisplayMediaManager::InitOptimalAudioFormat( |
| 224 const std::vector<wds::AudioCodec>& sink_codecs) { | 336 const std::vector<wds::AudioCodec>& sink_codecs) { |
| 225 for (const wds::AudioCodec& codec : sink_codecs) { | 337 for (const wds::AudioCodec& codec : sink_codecs) { |
| 226 // MediaStreamTrack contains LPCM audio. | 338 // MediaStreamTrack contains LPCM audio. |
| 227 if (codec.format == wds::LPCM) { | 339 if (codec.format == wds::LPCM) { |
| 228 optimal_audio_codec_ = codec; | 340 optimal_audio_codec_ = codec; |
| 229 // Picking a single mode. | 341 // Picking a single mode. |
| 230 wds::AudioModes optimal_mode; | 342 wds::AudioModes optimal_mode; |
| 231 if (codec.modes.test(wds::LPCM_44_1K_16B_2CH)) | 343 if (codec.modes.test(wds::LPCM_44_1K_16B_2CH)) |
| 232 optimal_mode.set(wds::LPCM_44_1K_16B_2CH); | 344 optimal_mode.set(wds::LPCM_44_1K_16B_2CH); |
| 233 else | 345 else |
| 234 optimal_mode.set(wds::LPCM_48K_16B_2CH); | 346 optimal_mode.set(wds::LPCM_48K_16B_2CH); |
| 235 optimal_audio_codec_.modes = optimal_mode; | 347 optimal_audio_codec_.modes = optimal_mode; |
| 236 return true; | 348 return true; |
| 237 } | 349 } |
| 238 } | 350 } |
| 239 error_callback_.Run(kErrorSinkCannotPlayAudio); | 351 error_callback_.Run(kErrorSinkCannotPlayAudio); |
| 240 return false; | 352 return false; |
| 241 } | 353 } |
| 242 | 354 |
| 243 wds::AudioCodec WiFiDisplayMediaManager::GetOptimalAudioFormat() const { | 355 wds::AudioCodec WiFiDisplayMediaManager::GetOptimalAudioFormat() const { |
| 244 return optimal_audio_codec_; | 356 return optimal_audio_codec_; |
| 245 } | 357 } |
| 246 | 358 |
| 359 void WiFiDisplayMediaManager::SendIDRPicture() { | |
| 360 DCHECK(player_); | |
| 361 io_task_runner_->PostTask(FROM_HERE, | |
| 362 base::Bind(&WiFiDisplayMediaPipeline::RequestIDRPicture, | |
| 363 base::Unretained(player_))); | |
| 364 } | |
| 365 | |
| 366 std::string WiFiDisplayMediaManager::GetSessionId() const { | |
| 367 if (session_id_.empty()) | |
| 368 session_id_ = base::RandBytesAsString(8); | |
| 369 return session_id_; | |
| 370 } | |
| 371 | |
| 372 void WiFiDisplayMediaManager::OnPlayerCreated( | |
| 373 scoped_ptr<WiFiDisplayMediaPipeline> player) { | |
| 374 DCHECK(player); | |
| 375 DCHECK(content::RenderThread::Get()); | |
| 376 player_ = player.release(); | |
| 377 | |
| 378 auto completion_callback = base::Bind( | |
| 379 &WiFiDisplayMediaManager::OnMediaPipelineInitialized, | |
| 380 weak_factory_.GetWeakPtr()); | |
| 381 | |
| 382 io_task_runner_->PostTask(FROM_HERE, | |
| 383 base::Bind(&WiFiDisplayMediaPipeline::Initialize, | |
| 384 base::Unretained(player_), | |
| 385 media::BindToCurrentLoop(completion_callback))); | |
| 386 } | |
| 387 | |
| 388 void WiFiDisplayMediaManager::OnMediaPipelineInitialized(bool success) { | |
| 389 DCHECK(content::RenderThread::Get()); | |
| 390 is_initialized_ = success; | |
| 391 | |
| 392 if (!is_initialized_) { | |
| 393 error_callback_.Run(kErrorMediaPipelineFailure); | |
| 394 return; | |
| 395 } | |
| 396 | |
| 397 if (is_playing_) | |
| 398 Play(); | |
| 399 } | |
| 400 | |
| 401 | |
| 247 } // namespace extensions | 402 } // namespace extensions |
| OLD | NEW |