Chromium Code Reviews| Index: remoting/test/test_video_renderer.cc |
| diff --git a/remoting/test/test_video_renderer.cc b/remoting/test/test_video_renderer.cc |
| index 864356a112cb3ac37cd152516ec8cab26ad36ea5..8a9f9e644cfaa0b547915c1004af1d424e826829 100644 |
| --- a/remoting/test/test_video_renderer.cc |
| +++ b/remoting/test/test_video_renderer.cc |
| @@ -4,46 +4,259 @@ |
| #include "remoting/test/test_video_renderer.h" |
| +#include "base/bind.h" |
| #include "base/logging.h" |
| +#include "base/synchronization/lock.h" |
| +#include "base/thread_task_runner_handle.h" |
| +#include "base/threading/thread.h" |
| +#include "remoting/codec/video_decoder.h" |
| +#include "remoting/codec/video_decoder_verbatim.h" |
| +#include "remoting/codec/video_decoder_vpx.h" |
| #include "remoting/proto/video.pb.h" |
| +#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" |
| +#include "third_party/webrtc/modules/desktop_capture/desktop_region.h" |
| namespace remoting { |
| namespace test { |
| -TestVideoRenderer::TestVideoRenderer() : video_frames_processed_(0) { |
| +// Implements video decoding functionality. |
| +class TestVideoRenderer::Core { |
| + public: |
| + explicit Core(base::WeakPtr<TestVideoRenderer> renderer); |
| + ~Core(); |
| + |
| + // Initializes the internal structures of the class. |
| + void Initialize(); |
| + |
| + // Used to decode video packets. |
| + void ProcessVideoPacket(scoped_ptr<VideoPacket> packet); |
| + |
| + // Set negotiated Codec for decoder. |
| + void SetDecoder(const protocol::ChannelConfig::Codec codec); |
| + |
| + // Returns a copy of the current buffer. |
| + scoped_ptr<webrtc::DesktopFrame> GetBufferForTest() const; |
| + |
| + private: |
| + // Used to ensure TestVideoRenderer::Core methods are called on the same |
| + // thread. |
| + base::ThreadChecker thread_checker_; |
| + |
| + // Used to decode video packets. |
| + scoped_ptr<VideoDecoder> decoder_; |
| + |
| + // Updated region of the current desktop frame compared to previous one. |
| + webrtc::DesktopRegion updated_region_; |
| + |
| + // Screen size of the remote host. |
| + webrtc::DesktopSize screen_size_; |
| + |
| + // Holding a WeakPtr to the TestVideoRenderer |
| + base::WeakPtr<TestVideoRenderer> renderer_; |
| + |
| + // Used to post tasks back to main thread. |
| + scoped_refptr<base::SingleThreadTaskRunner> renderer_task_runner_; |
|
joedow
2015/06/30 17:52:27
Can you rename 'renderer_task_runner_' to 'main_ta
liaoyuke
2015/06/30 19:34:59
Done.
|
| + |
| + // Used to store decoded video frame. |
| + scoped_ptr<webrtc::DesktopFrame> buffer_; |
| + |
| + // Protects access to |buffer_|. |
| + mutable base::Lock lock_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(Core); |
| +}; |
| + |
| +TestVideoRenderer::Core::Core(base::WeakPtr<TestVideoRenderer> renderer) |
| + : renderer_(renderer), |
| + renderer_task_runner_(base::ThreadTaskRunnerHandle::Get()) { |
| + thread_checker_.DetachFromThread(); |
| +} |
| + |
| +TestVideoRenderer::Core::~Core() { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| +} |
| + |
| +void TestVideoRenderer::Core::Initialize() { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| +} |
| + |
| +void TestVideoRenderer::Core::SetDecoder( |
| + const protocol::ChannelConfig::Codec codec) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| + if (decoder_) { |
| + LOG(WARNING) << "Decoder is set more than once"; |
| + } |
| + |
| + switch (codec) { |
| + case protocol::ChannelConfig::CODEC_VP8: { |
| + DVLOG(2) << "Test Video Renderer will use VP8 decoder"; |
| + decoder_ = VideoDecoderVpx::CreateForVP8(); |
| + break; |
| + } |
| + case protocol::ChannelConfig::CODEC_VP9: { |
| + DVLOG(2) << "Test Video Renderer will use VP9 decoder"; |
| + decoder_ = VideoDecoderVpx::CreateForVP9(); |
| + break; |
| + } |
| + case protocol::ChannelConfig::CODEC_VERBATIM: { |
| + DVLOG(2) << "Test Video Renderer will use VERBATIM decoder"; |
| + decoder_.reset(new VideoDecoderVerbatim()); |
| + break; |
| + } |
| + default: { |
| + NOTREACHED() << "Unsupported codec: " << codec; |
| + } |
| + } |
| +} |
| + |
| +scoped_ptr<webrtc::DesktopFrame> |
| + TestVideoRenderer::Core::GetBufferForTest() const { |
| + base::AutoLock auto_lock(lock_); |
| + DCHECK(buffer_); |
| + return make_scoped_ptr(webrtc::BasicDesktopFrame::CopyOf(*buffer_.get())); |
| +} |
| + |
| +void TestVideoRenderer::Core::ProcessVideoPacket( |
| + scoped_ptr<VideoPacket> packet) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + DCHECK(decoder_); |
| + DCHECK(packet); |
| + |
| + DVLOG(2) << "TestVideoRenderer::Core::ProcessVideoPacket() Called"; |
| + |
| + // Screen size is attached on the first packet as well as when the |
| + // host screen is resized. |
| + if (packet->format().has_screen_width() && |
| + packet->format().has_screen_height()) { |
| + webrtc::DesktopSize source_size(packet->format().screen_width(), |
| + packet->format().screen_height()); |
| + if (!screen_size_.equals(source_size)) { |
| + screen_size_ = source_size; |
| + decoder_->Initialize(screen_size_); |
| + buffer_.reset(new webrtc::BasicDesktopFrame(screen_size_)); |
| + } |
| + } |
| + |
| + // To make life easier, assume that the desktop shape is a single rectangle. |
| + packet->clear_use_desktop_shape(); |
| + if (!decoder_->DecodePacket(*packet.get())) { |
| + LOG(ERROR) << "Decoder::DecodePacket() failed."; |
| + return; |
| + } |
| + |
| + { |
| + base::AutoLock auto_lock(lock_); |
| + |
| + // Render the decoded packet and write results to the buffer. |
| + // Note that the |updated_region_| maintains the changed regions compared to |
| + // previous video frame. |
| + decoder_->RenderFrame(screen_size_, |
| + webrtc::DesktopRect::MakeWH(screen_size_.width(), |
| + screen_size_.height()), buffer_->data(), |
| + buffer_->stride(), &updated_region_); |
| + } |
| + |
| + renderer_task_runner_->PostTask( |
| + FROM_HERE, base::Bind(&TestVideoRenderer::OnPacketDone, renderer_)); |
|
joedow
2015/06/30 17:52:27
This would be a lot cleaner if we reuse the done c
liaoyuke
2015/06/30 19:34:59
Done.
|
| +} |
| + |
| +TestVideoRenderer::TestVideoRenderer() |
| + : video_decode_thread_( |
| + new base::Thread("TestVideoRendererVideoDecodingThread")), |
| + weak_factory_(this) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| + core_.reset(new Core(weak_factory_.GetWeakPtr())); |
| + if (!video_decode_thread_->Start()) { |
| + LOG(ERROR) << "Cannot start TestVideoRenderer"; |
| + } else { |
| + video_decode_task_runner_ = video_decode_thread_->task_runner(); |
| + video_decode_task_runner_->PostTask(FROM_HERE, base::Bind(&Core::Initialize, |
| + base::Unretained(core_.get()))); |
| + } |
| } |
| TestVideoRenderer::~TestVideoRenderer() { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| + video_decode_task_runner_->DeleteSoon(FROM_HERE, core_.release()); |
| + |
| + // The thread's message loop will run until it runs out of work. |
| + video_decode_thread_->Stop(); |
| } |
| void TestVideoRenderer::OnSessionConfig(const protocol::SessionConfig& config) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| DVLOG(2) << "TestVideoRenderer::OnSessionConfig() Called"; |
| + protocol::ChannelConfig::Codec codec = config.video_config().codec; |
| + SetDecoder(codec); |
| } |
| ChromotingStats* TestVideoRenderer::GetStats() { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| DVLOG(2) << "TestVideoRenderer::GetStats() Called"; |
| return nullptr; |
| } |
| protocol::VideoStub* TestVideoRenderer::GetVideoStub() { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| DVLOG(2) << "TestVideoRenderer::GetVideoStub() Called"; |
| return this; |
| } |
| void TestVideoRenderer::ProcessVideoPacket(scoped_ptr<VideoPacket> video_packet, |
| const base::Closure& done) { |
| - if (!video_packet->data().empty()) { |
| - // If the video frame was not a keep alive frame (i.e. not empty) then |
| - // count it. |
| - DVLOG(2) << "Video Packet Processed, Total: " << ++video_frames_processed_; |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + DCHECK(video_decode_task_runner_) << "Failed to start video decode thread"; |
| + |
| + if (video_packet->has_data() && video_packet->data().size() != 0) { |
| + DVLOG(2) << "process video packet is called!"; |
| + |
| + // Post video process task to the video decode thread. |
| + base::Closure process_video_task = base::Bind( |
| + &TestVideoRenderer::Core::ProcessVideoPacket, |
| + base::Unretained(core_.get()), base::Passed(&video_packet)); |
| + video_decode_task_runner_->PostTask(FROM_HERE, process_video_task); |
| } else { |
| // Log at a high verbosity level as we receive empty packets frequently and |
| // they can clutter up the debug output if the level is set too low. |
| DVLOG(3) << "Empty Video Packet received."; |
| } |
| - |
| done.Run(); |
| } |
| +void TestVideoRenderer::SetDecoder(const protocol::ChannelConfig::Codec codec) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| + DVLOG(2) << "TestVideoRenderer::SetDecoder() Called"; |
| + video_decode_task_runner_->PostTask( |
| + FROM_HERE, base::Bind(&Core::SetDecoder, base::Unretained(core_.get()), |
| + codec)); |
| +} |
| + |
| +scoped_ptr<webrtc::DesktopFrame> TestVideoRenderer::GetBufferForTest() const { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| + return core_->GetBufferForTest(); |
| +} |
| + |
| +void TestVideoRenderer::WaitForPacketDone(const base::Closure& done) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| + main_closure_ = done; |
| +} |
| + |
| +void TestVideoRenderer::OnPacketDone() { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| + if (!main_closure_.is_null()) { |
| + main_closure_.Run(); |
| + } |
| +} |
| + |
| } // namespace test |
| } // namespace remoting |