Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 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 "remoting/test/test_video_renderer.h" | 5 #include "remoting/test/test_video_renderer.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | |
| 7 #include "base/logging.h" | 8 #include "base/logging.h" |
| 9 #include "base/synchronization/lock.h" | |
| 10 #include "base/thread_task_runner_handle.h" | |
| 11 #include "base/threading/thread.h" | |
| 12 #include "remoting/codec/video_decoder.h" | |
| 13 #include "remoting/codec/video_decoder_verbatim.h" | |
| 14 #include "remoting/codec/video_decoder_vpx.h" | |
| 8 #include "remoting/proto/video.pb.h" | 15 #include "remoting/proto/video.pb.h" |
| 16 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" | |
| 17 #include "third_party/webrtc/modules/desktop_capture/desktop_region.h" | |
| 9 | 18 |
| 10 namespace remoting { | 19 namespace remoting { |
| 11 namespace test { | 20 namespace test { |
| 12 | 21 |
| 13 TestVideoRenderer::TestVideoRenderer() : video_frames_processed_(0) { | 22 // Implements video decoding functionality. |
| 23 class TestVideoRenderer::Core { | |
| 24 public: | |
| 25 explicit Core(base::WeakPtr<TestVideoRenderer> renderer); | |
| 26 ~Core(); | |
| 27 | |
| 28 // Initializes the internal structures of the class. | |
| 29 void Initialize(); | |
| 30 | |
| 31 // Used to decode video packets. | |
| 32 void ProcessVideoPacket(scoped_ptr<VideoPacket> packet); | |
| 33 | |
| 34 // Set negotiated Codec for decoder. | |
| 35 void SetDecoder(const protocol::ChannelConfig::Codec codec); | |
| 36 | |
| 37 // Returns a copy of the current buffer. | |
| 38 scoped_ptr<webrtc::DesktopFrame> GetBufferForTest() const; | |
| 39 | |
| 40 private: | |
| 41 // Used to ensure TestVideoRenderer::Core methods are called on the same | |
| 42 // thread. | |
| 43 base::ThreadChecker thread_checker_; | |
| 44 | |
| 45 // Used to decode video packets. | |
| 46 scoped_ptr<VideoDecoder> decoder_; | |
| 47 | |
| 48 // Updated region of the current desktop frame compared to previous one. | |
| 49 webrtc::DesktopRegion updated_region_; | |
| 50 | |
| 51 // Screen size of the remote host. | |
| 52 webrtc::DesktopSize screen_size_; | |
| 53 | |
| 54 // Holding a WeakPtr to the TestVideoRenderer | |
| 55 base::WeakPtr<TestVideoRenderer> renderer_; | |
| 56 | |
| 57 // Used to post tasks back to main thread. | |
| 58 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.
| |
| 59 | |
| 60 // Used to store decoded video frame. | |
| 61 scoped_ptr<webrtc::DesktopFrame> buffer_; | |
| 62 | |
| 63 // Protects access to |buffer_|. | |
| 64 mutable base::Lock lock_; | |
| 65 | |
| 66 DISALLOW_COPY_AND_ASSIGN(Core); | |
| 67 }; | |
| 68 | |
| 69 TestVideoRenderer::Core::Core(base::WeakPtr<TestVideoRenderer> renderer) | |
| 70 : renderer_(renderer), | |
| 71 renderer_task_runner_(base::ThreadTaskRunnerHandle::Get()) { | |
| 72 thread_checker_.DetachFromThread(); | |
| 73 } | |
| 74 | |
| 75 TestVideoRenderer::Core::~Core() { | |
| 76 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 77 } | |
| 78 | |
| 79 void TestVideoRenderer::Core::Initialize() { | |
| 80 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 81 } | |
| 82 | |
| 83 void TestVideoRenderer::Core::SetDecoder( | |
| 84 const protocol::ChannelConfig::Codec codec) { | |
| 85 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 86 | |
| 87 if (decoder_) { | |
| 88 LOG(WARNING) << "Decoder is set more than once"; | |
| 89 } | |
| 90 | |
| 91 switch (codec) { | |
| 92 case protocol::ChannelConfig::CODEC_VP8: { | |
| 93 DVLOG(2) << "Test Video Renderer will use VP8 decoder"; | |
| 94 decoder_ = VideoDecoderVpx::CreateForVP8(); | |
| 95 break; | |
| 96 } | |
| 97 case protocol::ChannelConfig::CODEC_VP9: { | |
| 98 DVLOG(2) << "Test Video Renderer will use VP9 decoder"; | |
| 99 decoder_ = VideoDecoderVpx::CreateForVP9(); | |
| 100 break; | |
| 101 } | |
| 102 case protocol::ChannelConfig::CODEC_VERBATIM: { | |
| 103 DVLOG(2) << "Test Video Renderer will use VERBATIM decoder"; | |
| 104 decoder_.reset(new VideoDecoderVerbatim()); | |
| 105 break; | |
| 106 } | |
| 107 default: { | |
| 108 NOTREACHED() << "Unsupported codec: " << codec; | |
| 109 } | |
| 110 } | |
| 111 } | |
| 112 | |
| 113 scoped_ptr<webrtc::DesktopFrame> | |
| 114 TestVideoRenderer::Core::GetBufferForTest() const { | |
| 115 base::AutoLock auto_lock(lock_); | |
| 116 DCHECK(buffer_); | |
| 117 return make_scoped_ptr(webrtc::BasicDesktopFrame::CopyOf(*buffer_.get())); | |
| 118 } | |
| 119 | |
| 120 void TestVideoRenderer::Core::ProcessVideoPacket( | |
| 121 scoped_ptr<VideoPacket> packet) { | |
| 122 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 123 DCHECK(decoder_); | |
| 124 DCHECK(packet); | |
| 125 | |
| 126 DVLOG(2) << "TestVideoRenderer::Core::ProcessVideoPacket() Called"; | |
| 127 | |
| 128 // Screen size is attached on the first packet as well as when the | |
| 129 // host screen is resized. | |
| 130 if (packet->format().has_screen_width() && | |
| 131 packet->format().has_screen_height()) { | |
| 132 webrtc::DesktopSize source_size(packet->format().screen_width(), | |
| 133 packet->format().screen_height()); | |
| 134 if (!screen_size_.equals(source_size)) { | |
| 135 screen_size_ = source_size; | |
| 136 decoder_->Initialize(screen_size_); | |
| 137 buffer_.reset(new webrtc::BasicDesktopFrame(screen_size_)); | |
| 138 } | |
| 139 } | |
| 140 | |
| 141 // To make life easier, assume that the desktop shape is a single rectangle. | |
| 142 packet->clear_use_desktop_shape(); | |
| 143 if (!decoder_->DecodePacket(*packet.get())) { | |
| 144 LOG(ERROR) << "Decoder::DecodePacket() failed."; | |
| 145 return; | |
| 146 } | |
| 147 | |
| 148 { | |
| 149 base::AutoLock auto_lock(lock_); | |
| 150 | |
| 151 // Render the decoded packet and write results to the buffer. | |
| 152 // Note that the |updated_region_| maintains the changed regions compared to | |
| 153 // previous video frame. | |
| 154 decoder_->RenderFrame(screen_size_, | |
| 155 webrtc::DesktopRect::MakeWH(screen_size_.width(), | |
| 156 screen_size_.height()), buffer_->data(), | |
| 157 buffer_->stride(), &updated_region_); | |
| 158 } | |
| 159 | |
| 160 renderer_task_runner_->PostTask( | |
| 161 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.
| |
| 162 } | |
| 163 | |
| 164 TestVideoRenderer::TestVideoRenderer() | |
| 165 : video_decode_thread_( | |
| 166 new base::Thread("TestVideoRendererVideoDecodingThread")), | |
| 167 weak_factory_(this) { | |
| 168 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 169 | |
| 170 core_.reset(new Core(weak_factory_.GetWeakPtr())); | |
| 171 if (!video_decode_thread_->Start()) { | |
| 172 LOG(ERROR) << "Cannot start TestVideoRenderer"; | |
| 173 } else { | |
| 174 video_decode_task_runner_ = video_decode_thread_->task_runner(); | |
| 175 video_decode_task_runner_->PostTask(FROM_HERE, base::Bind(&Core::Initialize, | |
| 176 base::Unretained(core_.get()))); | |
| 177 } | |
| 14 } | 178 } |
| 15 | 179 |
| 16 TestVideoRenderer::~TestVideoRenderer() { | 180 TestVideoRenderer::~TestVideoRenderer() { |
| 181 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 182 | |
| 183 video_decode_task_runner_->DeleteSoon(FROM_HERE, core_.release()); | |
| 184 | |
| 185 // The thread's message loop will run until it runs out of work. | |
| 186 video_decode_thread_->Stop(); | |
| 17 } | 187 } |
| 18 | 188 |
| 19 void TestVideoRenderer::OnSessionConfig(const protocol::SessionConfig& config) { | 189 void TestVideoRenderer::OnSessionConfig(const protocol::SessionConfig& config) { |
| 190 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 191 | |
| 20 DVLOG(2) << "TestVideoRenderer::OnSessionConfig() Called"; | 192 DVLOG(2) << "TestVideoRenderer::OnSessionConfig() Called"; |
| 193 protocol::ChannelConfig::Codec codec = config.video_config().codec; | |
| 194 SetDecoder(codec); | |
| 21 } | 195 } |
| 22 | 196 |
| 23 ChromotingStats* TestVideoRenderer::GetStats() { | 197 ChromotingStats* TestVideoRenderer::GetStats() { |
| 198 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 199 | |
| 24 DVLOG(2) << "TestVideoRenderer::GetStats() Called"; | 200 DVLOG(2) << "TestVideoRenderer::GetStats() Called"; |
| 25 return nullptr; | 201 return nullptr; |
| 26 } | 202 } |
| 27 | 203 |
| 28 protocol::VideoStub* TestVideoRenderer::GetVideoStub() { | 204 protocol::VideoStub* TestVideoRenderer::GetVideoStub() { |
| 205 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 206 | |
| 29 DVLOG(2) << "TestVideoRenderer::GetVideoStub() Called"; | 207 DVLOG(2) << "TestVideoRenderer::GetVideoStub() Called"; |
| 30 return this; | 208 return this; |
| 31 } | 209 } |
| 32 | 210 |
| 33 void TestVideoRenderer::ProcessVideoPacket(scoped_ptr<VideoPacket> video_packet, | 211 void TestVideoRenderer::ProcessVideoPacket(scoped_ptr<VideoPacket> video_packet, |
| 34 const base::Closure& done) { | 212 const base::Closure& done) { |
| 35 if (!video_packet->data().empty()) { | 213 DCHECK(thread_checker_.CalledOnValidThread()); |
| 36 // If the video frame was not a keep alive frame (i.e. not empty) then | 214 DCHECK(video_decode_task_runner_) << "Failed to start video decode thread"; |
| 37 // count it. | 215 |
| 38 DVLOG(2) << "Video Packet Processed, Total: " << ++video_frames_processed_; | 216 if (video_packet->has_data() && video_packet->data().size() != 0) { |
| 217 DVLOG(2) << "process video packet is called!"; | |
| 218 | |
| 219 // Post video process task to the video decode thread. | |
| 220 base::Closure process_video_task = base::Bind( | |
| 221 &TestVideoRenderer::Core::ProcessVideoPacket, | |
| 222 base::Unretained(core_.get()), base::Passed(&video_packet)); | |
| 223 video_decode_task_runner_->PostTask(FROM_HERE, process_video_task); | |
| 39 } else { | 224 } else { |
| 40 // Log at a high verbosity level as we receive empty packets frequently and | 225 // Log at a high verbosity level as we receive empty packets frequently and |
| 41 // they can clutter up the debug output if the level is set too low. | 226 // they can clutter up the debug output if the level is set too low. |
| 42 DVLOG(3) << "Empty Video Packet received."; | 227 DVLOG(3) << "Empty Video Packet received."; |
| 43 } | 228 } |
| 44 | |
| 45 done.Run(); | 229 done.Run(); |
| 46 } | 230 } |
| 47 | 231 |
| 232 void TestVideoRenderer::SetDecoder(const protocol::ChannelConfig::Codec codec) { | |
| 233 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 234 | |
| 235 DVLOG(2) << "TestVideoRenderer::SetDecoder() Called"; | |
| 236 video_decode_task_runner_->PostTask( | |
| 237 FROM_HERE, base::Bind(&Core::SetDecoder, base::Unretained(core_.get()), | |
| 238 codec)); | |
| 239 } | |
| 240 | |
| 241 scoped_ptr<webrtc::DesktopFrame> TestVideoRenderer::GetBufferForTest() const { | |
| 242 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 243 | |
| 244 return core_->GetBufferForTest(); | |
| 245 } | |
| 246 | |
| 247 void TestVideoRenderer::WaitForPacketDone(const base::Closure& done) { | |
| 248 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 249 | |
| 250 main_closure_ = done; | |
| 251 } | |
| 252 | |
| 253 void TestVideoRenderer::OnPacketDone() { | |
| 254 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 255 | |
| 256 if (!main_closure_.is_null()) { | |
| 257 main_closure_.Run(); | |
| 258 } | |
| 259 } | |
| 260 | |
| 48 } // namespace test | 261 } // namespace test |
| 49 } // namespace remoting | 262 } // namespace remoting |
| OLD | NEW |