Chromium Code Reviews| Index: extensions/renderer/api/display_source/wifi_display/wifi_display_media_manager.cc |
| diff --git a/extensions/renderer/api/display_source/wifi_display/wifi_display_media_manager.cc b/extensions/renderer/api/display_source/wifi_display/wifi_display_media_manager.cc |
| index f77571d58a2cb4a38d563e4a0ec3d843b50d44b1..f06558f287bf3a6d5b25169d24239833ccb945e9 100644 |
| --- a/extensions/renderer/api/display_source/wifi_display/wifi_display_media_manager.cc |
| +++ b/extensions/renderer/api/display_source/wifi_display/wifi_display_media_manager.cc |
| @@ -6,7 +6,15 @@ |
| #include "base/logging.h" |
| #include "base/rand_util.h" |
| +#include "base/task_runner_util.h" |
| #include "content/public/renderer/media_stream_utils.h" |
| +#include "content/public/renderer/media_stream_video_sink.h" |
| +#include "content/public/renderer/render_thread.h" |
| +#include "content/public/renderer/video_encode_accelerator.h" |
| +#include "extensions/common/mojo/wifi_display_session_service.mojom.h" |
| +#include "extensions/renderer/api/display_source/wifi_display/wifi_display_elementary_stream_info.h" |
| +#include "extensions/renderer/api/display_source/wifi_display/wifi_display_media_pipeline.h" |
| +#include "media/base/bind_to_current_loop.h" |
| namespace extensions { |
| @@ -18,37 +26,110 @@ const char kErrorSinkCannotPlayVideo[] = |
| "The sink cannot play video from the given MediaStreamTrack object"; |
| const char kErrorSinkCannotPlayAudio[] = |
| "The sink cannot play audio from the given MediaStreamTrack object"; |
| +const char kErrorMediaPipelineFailure[] = |
| + "Failed to initialize media pipeline for the session"; |
| } // namespace |
| +class WiFiDisplayVideoSink : public content::MediaStreamVideoSink { |
| + public: |
| + WiFiDisplayVideoSink( |
| + const blink::WebMediaStreamTrack& track, |
| + const content::VideoCaptureDeliverFrameCB& callback) |
| + : track_(track), |
| + sink_added_(false), |
| + callback_(callback) { |
| + } |
| + |
| + ~WiFiDisplayVideoSink() override { |
| + Stop(); |
| + } |
| + |
| + void Start() { |
| + DCHECK(!sink_added_); |
| + sink_added_ = true; |
| + // Callback is invoked on IO thread. |
| + AddToVideoTrack(this, callback_, track_); |
| + } |
| + |
| + void Stop() { |
| + if (sink_added_) |
| + RemoveFromVideoTrack(this, track_); |
| + } |
| + |
| + private: |
| + blink::WebMediaStreamTrack track_; |
| + bool sink_added_; |
| + content::VideoCaptureDeliverFrameCB callback_; |
| + DISALLOW_COPY_AND_ASSIGN(WiFiDisplayVideoSink); |
| +}; |
| + |
| WiFiDisplayMediaManager::WiFiDisplayMediaManager( |
| const blink::WebMediaStreamTrack& video_track, |
| const blink::WebMediaStreamTrack& audio_track, |
| const ErrorCallback& error_callback) |
| : video_track_(video_track), |
| audio_track_(audio_track), |
| - error_callback_(error_callback) { |
| + player_(nullptr), |
| + io_task_runner_(content::RenderThread::Get()->GetIOMessageLoopProxy()), |
| + error_callback_(error_callback), |
| + is_playing_(false), |
| + is_initialized_(false), |
| + weak_factory_(this) { |
| DCHECK(!video_track.isNull() || !audio_track.isNull()); |
| DCHECK(!error_callback_.is_null()); |
| } |
| WiFiDisplayMediaManager::~WiFiDisplayMediaManager() { |
| + Teardown(); |
| } |
| void WiFiDisplayMediaManager::Play() { |
| - NOTIMPLEMENTED(); |
| + is_playing_ = true; |
| + if (!player_) { |
| + base::PostTaskAndReplyWithResult(io_task_runner_.get(), FROM_HERE, |
| + base::Bind( |
| + &WiFiDisplayMediaPipeline::Create, |
| + GetSessionType(), |
| + video_encoder_parameters_, |
| + optimal_audio_codec_, |
| + media::BindToCurrentLoop(error_callback_)), |
| + base::Bind(&WiFiDisplayMediaManager::OnPlayerCreated, |
| + weak_factory_.GetWeakPtr())); |
| + return; |
| + } |
| + |
| + if (!is_initialized_) { |
| + return; // Waiting for initialization being completed. |
| + } |
| + |
| + if (!video_track_.isNull()) { |
| + // To be called on IO thread. |
| + auto on_raw_video_frame = base::Bind( |
| + &WiFiDisplayMediaPipeline::InsertRawVideoFrame, |
| + base::Unretained(player_)); |
| + video_sink_.reset( |
| + new WiFiDisplayVideoSink(video_track_, on_raw_video_frame)); |
| + video_sink_->Start(); |
| + } |
| } |
| void WiFiDisplayMediaManager::Teardown() { |
| - NOTIMPLEMENTED(); |
| + Pause(); |
| + if (player_) { |
| + io_task_runner_->DeleteSoon(FROM_HERE, player_); |
| + player_ = nullptr; |
| + } |
| + is_initialized_ = false; |
| + session_id_.clear(); |
| } |
| void WiFiDisplayMediaManager::Pause() { |
| - NOTIMPLEMENTED(); |
| + is_playing_ = false; |
| + video_sink_.reset(); |
| } |
| bool WiFiDisplayMediaManager::IsPaused() const { |
| - NOTIMPLEMENTED(); |
| - return true; |
| + return !is_playing_; |
| } |
| wds::SessionType WiFiDisplayMediaManager::GetSessionType() const { |
| @@ -193,14 +274,32 @@ wds::H264VideoFormat WiFiDisplayMediaManager::GetOptimalVideoFormat() const { |
| return optimal_video_format_; |
| } |
| -void WiFiDisplayMediaManager::SendIDRPicture() { |
| - NOTIMPLEMENTED(); |
| +namespace { |
| + |
| +int GetBitRate(const gfx::Size& frame_size) { |
| + DCHECK_GE(frame_size.height(), 360); |
| + if (frame_size.height() < 720) |
| + return 2500000; |
| + if (frame_size.height() < 1080) |
| + return 5000000; |
| + return 8000000; |
| } |
| -std::string WiFiDisplayMediaManager::GetSessionId() const { |
| - return base::RandBytesAsString(8); |
| +void CreateVideoEncodeMemory( |
| + size_t size, |
| + const WiFiDisplayVideoEncoder::ReceiveEncodeMemoryCallback& callback) { |
| + DCHECK(content::RenderThread::Get()); |
| + |
| + std::unique_ptr<base::SharedMemory> shm = |
| + content::RenderThread::Get()->HostAllocateSharedMemoryBuffer(size); |
| + if (!shm || !shm->Map(size)) { |
| + NOTREACHED() << "Shared memory allocation or map failed"; |
| + } |
| + callback.Run(std::move(shm)); |
| } |
| +} // namespace |
| + |
| bool WiFiDisplayMediaManager::InitOptimalVideoFormat( |
| const wds::NativeVideoFormat& sink_native_format, |
| const std::vector<wds::H264VideoCodec>& sink_supported_codecs) { |
| @@ -216,6 +315,17 @@ bool WiFiDisplayMediaManager::InitOptimalVideoFormat( |
| error_callback_.Run(kErrorSinkCannotPlayVideo); |
| return false; |
| } |
| + video_encoder_parameters_.frame_size = capture_format->frame_size; |
| + video_encoder_parameters_.frame_rate = |
| + static_cast<int>(capture_format->frame_rate); |
| + video_encoder_parameters_.bit_rate = GetBitRate(capture_format->frame_size); |
| + video_encoder_parameters_.profile = optimal_video_format_.profile; |
| + video_encoder_parameters_.level = optimal_video_format_.level; |
| + video_encoder_parameters_.create_memory_callback = |
| + 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
|
| + 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
|
| + media::BindToCurrentLoop( |
| + base::Bind(&content::CreateVideoEncodeAccelerator)); |
| return true; |
| } |
| @@ -244,4 +354,47 @@ wds::AudioCodec WiFiDisplayMediaManager::GetOptimalAudioFormat() const { |
| return optimal_audio_codec_; |
| } |
| +void WiFiDisplayMediaManager::SendIDRPicture() { |
| + DCHECK(player_); |
| + io_task_runner_->PostTask(FROM_HERE, |
| + base::Bind(&WiFiDisplayMediaPipeline::RequestIDRPicture, |
| + base::Unretained(player_))); |
| +} |
| + |
| +std::string WiFiDisplayMediaManager::GetSessionId() const { |
| + if (session_id_.empty()) |
| + session_id_ = base::RandBytesAsString(8); |
| + return session_id_; |
| +} |
| + |
| +void WiFiDisplayMediaManager::OnPlayerCreated( |
| + std::unique_ptr<WiFiDisplayMediaPipeline> player) { |
| + DCHECK(player); |
| + DCHECK(content::RenderThread::Get()); |
| + player_ = player.release(); |
| + |
| + auto completion_callback = base::Bind( |
| + &WiFiDisplayMediaManager::OnMediaPipelineInitialized, |
| + weak_factory_.GetWeakPtr()); |
| + |
| + io_task_runner_->PostTask(FROM_HERE, |
| + base::Bind(&WiFiDisplayMediaPipeline::Initialize, |
| + base::Unretained(player_), |
| + media::BindToCurrentLoop(completion_callback))); |
| +} |
| + |
| +void WiFiDisplayMediaManager::OnMediaPipelineInitialized(bool success) { |
| + DCHECK(content::RenderThread::Get()); |
| + is_initialized_ = success; |
| + |
| + if (!is_initialized_) { |
| + error_callback_.Run(kErrorMediaPipelineFailure); |
| + return; |
| + } |
| + |
| + if (is_playing_) |
| + Play(); |
| +} |
| + |
| + |
| } // namespace extensions |