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 |