| 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/protocol/webrtc_video_capturer_adapter.h" | 5 #include "remoting/protocol/webrtc_video_capturer_adapter.h" |
| 6 | 6 |
| 7 #include <utility> | 7 #include <utility> |
| 8 | 8 |
| 9 #include "third_party/libjingle/source/talk/media/webrtc/webrtcvideoframe.h" |
| 10 #include "third_party/libyuv/include/libyuv/convert.h" |
| 9 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" | 11 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" |
| 10 | 12 |
| 11 namespace remoting { | 13 namespace remoting { |
| 14 namespace protocol { |
| 12 | 15 |
| 13 // Number of frames to be captured per second. | 16 // Number of frames to be captured per second. |
| 14 const int kFramesPerSec = 30; | 17 const int kFramesPerSec = 30; |
| 15 | 18 |
| 16 WebrtcVideoCapturerAdapter::WebrtcVideoCapturerAdapter( | 19 WebrtcVideoCapturerAdapter::WebrtcVideoCapturerAdapter( |
| 17 scoped_ptr<webrtc::DesktopCapturer> capturer) | 20 scoped_ptr<webrtc::DesktopCapturer> capturer) |
| 18 : desktop_capturer_(std::move(capturer)) { | 21 : desktop_capturer_(std::move(capturer)) { |
| 19 DCHECK(desktop_capturer_); | 22 DCHECK(desktop_capturer_); |
| 20 | 23 |
| 21 thread_checker_.DetachFromThread(); | |
| 22 | |
| 23 // Disable video adaptation since we don't intend to use it. | 24 // Disable video adaptation since we don't intend to use it. |
| 24 set_enable_video_adapter(false); | 25 set_enable_video_adapter(false); |
| 25 } | 26 } |
| 26 | 27 |
| 27 WebrtcVideoCapturerAdapter::~WebrtcVideoCapturerAdapter() { | 28 WebrtcVideoCapturerAdapter::~WebrtcVideoCapturerAdapter() { |
| 28 DCHECK(!capture_timer_); | 29 DCHECK(!capture_timer_); |
| 29 } | 30 } |
| 30 | 31 |
| 31 webrtc::SharedMemory* WebrtcVideoCapturerAdapter::CreateSharedMemory( | |
| 32 size_t size) { | |
| 33 return nullptr; | |
| 34 } | |
| 35 | |
| 36 void WebrtcVideoCapturerAdapter::OnCaptureCompleted( | |
| 37 webrtc::DesktopFrame* frame) { | |
| 38 scoped_ptr<webrtc::DesktopFrame> owned_frame(frame); | |
| 39 | |
| 40 // Drop the owned_frame if there were no changes. | |
| 41 if (!owned_frame || owned_frame->updated_region().is_empty()) { | |
| 42 owned_frame.reset(); | |
| 43 return; | |
| 44 } | |
| 45 | |
| 46 // Convert the webrtc::DesktopFrame to a cricket::CapturedFrame. | |
| 47 cricket::CapturedFrame captured_frame; | |
| 48 captured_frame.width = owned_frame->size().width(); | |
| 49 captured_frame.height = owned_frame->size().height(); | |
| 50 base::TimeTicks current_time = base::TimeTicks::Now(); | |
| 51 captured_frame.time_stamp = | |
| 52 current_time.ToInternalValue() * base::Time::kNanosecondsPerMicrosecond; | |
| 53 captured_frame.data = owned_frame->data(); | |
| 54 | |
| 55 // The data_size attribute must be set. If multiple formats are supported, | |
| 56 // this should be set appropriately for each one. | |
| 57 captured_frame.data_size = | |
| 58 (captured_frame.width * webrtc::DesktopFrame::kBytesPerPixel * 8 + 7) / | |
| 59 8 * captured_frame.height; | |
| 60 captured_frame.fourcc = cricket::FOURCC_ARGB; | |
| 61 | |
| 62 SignalFrameCaptured(this, &captured_frame); | |
| 63 } | |
| 64 | |
| 65 bool WebrtcVideoCapturerAdapter::GetBestCaptureFormat( | 32 bool WebrtcVideoCapturerAdapter::GetBestCaptureFormat( |
| 66 const cricket::VideoFormat& desired, | 33 const cricket::VideoFormat& desired, |
| 67 cricket::VideoFormat* best_format) { | 34 cricket::VideoFormat* best_format) { |
| 68 DCHECK(thread_checker_.CalledOnValidThread()); | 35 DCHECK(thread_checker_.CalledOnValidThread()); |
| 69 | 36 |
| 70 // For now, just used the desired width and height. | 37 // The |capture_format| passed to Start() is always ignored, so copy |
| 71 best_format->width = desired.width; | 38 // |best_format| to |desired_format|. |
| 72 best_format->height = desired.height; | 39 *best_format = desired; |
| 73 best_format->fourcc = cricket::FOURCC_ARGB; | |
| 74 best_format->interval = FPS_TO_INTERVAL(kFramesPerSec); | |
| 75 return true; | 40 return true; |
| 76 } | 41 } |
| 77 | 42 |
| 78 cricket::CaptureState WebrtcVideoCapturerAdapter::Start( | 43 cricket::CaptureState WebrtcVideoCapturerAdapter::Start( |
| 79 const cricket::VideoFormat& capture_format) { | 44 const cricket::VideoFormat& capture_format) { |
| 80 DCHECK(thread_checker_.CalledOnValidThread()); | 45 DCHECK(thread_checker_.CalledOnValidThread()); |
| 81 DCHECK(!capture_timer_); | 46 DCHECK(!capture_timer_); |
| 82 DCHECK_EQ(capture_format.fourcc, | |
| 83 (static_cast<uint32_t>(cricket::FOURCC_ARGB))); | |
| 84 | 47 |
| 85 if (!desktop_capturer_) { | 48 if (!desktop_capturer_) { |
| 86 VLOG(1) << "WebrtcVideoCapturerAdapter failed to start."; | 49 VLOG(1) << "WebrtcVideoCapturerAdapter failed to start."; |
| 87 return cricket::CS_FAILED; | 50 return cricket::CS_FAILED; |
| 88 } | 51 } |
| 89 | 52 |
| 90 // This is required to tell the cricket::VideoCapturer base class what the | |
| 91 // capture format will be. | |
| 92 SetCaptureFormat(&capture_format); | |
| 93 | |
| 94 desktop_capturer_->Start(this); | 53 desktop_capturer_->Start(this); |
| 95 | 54 |
| 96 capture_timer_.reset(new base::RepeatingTimer()); | 55 capture_timer_.reset(new base::RepeatingTimer()); |
| 97 capture_timer_->Start(FROM_HERE, | 56 capture_timer_->Start(FROM_HERE, |
| 98 base::TimeDelta::FromMicroseconds( | 57 base::TimeDelta::FromSeconds(1) / kFramesPerSec, this, |
| 99 GetCaptureFormat()->interval / | |
| 100 (base::Time::kNanosecondsPerMicrosecond)), | |
| 101 this, | |
| 102 &WebrtcVideoCapturerAdapter::CaptureNextFrame); | 58 &WebrtcVideoCapturerAdapter::CaptureNextFrame); |
| 103 | 59 |
| 104 return cricket::CS_RUNNING; | 60 return cricket::CS_RUNNING; |
| 105 } | 61 } |
| 106 | 62 |
| 107 // Similar to the base class implementation with some important differences: | 63 // Similar to the base class implementation with some important differences: |
| 108 // 1. Does not call either Stop() or Start(), as those would affect the state of | 64 // 1. Does not call either Stop() or Start(), as those would affect the state of |
| 109 // |desktop_capturer_|. | 65 // |desktop_capturer_|. |
| 110 // 2. Does not support unpausing after stopping the capturer. It is unclear | 66 // 2. Does not support unpausing after stopping the capturer. It is unclear |
| 111 // if that flow needs to be supported. | 67 // if that flow needs to be supported. |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 163 desktop_capturer_.reset(); | 119 desktop_capturer_.reset(); |
| 164 | 120 |
| 165 SetCaptureFormat(nullptr); | 121 SetCaptureFormat(nullptr); |
| 166 SetCaptureState(cricket::CS_STOPPED); | 122 SetCaptureState(cricket::CS_STOPPED); |
| 167 | 123 |
| 168 VLOG(1) << "WebrtcVideoCapturerAdapter stopped."; | 124 VLOG(1) << "WebrtcVideoCapturerAdapter stopped."; |
| 169 } | 125 } |
| 170 | 126 |
| 171 bool WebrtcVideoCapturerAdapter::IsRunning() { | 127 bool WebrtcVideoCapturerAdapter::IsRunning() { |
| 172 DCHECK(thread_checker_.CalledOnValidThread()); | 128 DCHECK(thread_checker_.CalledOnValidThread()); |
| 173 | |
| 174 return capture_timer_->IsRunning(); | 129 return capture_timer_->IsRunning(); |
| 175 } | 130 } |
| 176 | 131 |
| 177 bool WebrtcVideoCapturerAdapter::IsScreencast() const { | 132 bool WebrtcVideoCapturerAdapter::IsScreencast() const { |
| 178 return true; | 133 return true; |
| 179 } | 134 } |
| 180 | 135 |
| 181 bool WebrtcVideoCapturerAdapter::GetPreferredFourccs( | 136 bool WebrtcVideoCapturerAdapter::GetPreferredFourccs( |
| 182 std::vector<uint32_t>* fourccs) { | 137 std::vector<uint32_t>* fourccs) { |
| 138 return false; |
| 139 } |
| 140 |
| 141 webrtc::SharedMemory* WebrtcVideoCapturerAdapter::CreateSharedMemory( |
| 142 size_t size) { |
| 143 return nullptr; |
| 144 } |
| 145 |
| 146 void WebrtcVideoCapturerAdapter::OnCaptureCompleted( |
| 147 webrtc::DesktopFrame* frame) { |
| 183 DCHECK(thread_checker_.CalledOnValidThread()); | 148 DCHECK(thread_checker_.CalledOnValidThread()); |
| 184 if (!fourccs) | 149 |
| 185 return false; | 150 DCHECK(capture_pending_); |
| 186 fourccs->push_back(cricket::FOURCC_ARGB); | 151 capture_pending_ = false; |
| 187 return true; | 152 |
| 153 scoped_ptr<webrtc::DesktopFrame> owned_frame(frame); |
| 154 |
| 155 // Drop the frame if there were no changes. |
| 156 if (!owned_frame || owned_frame->updated_region().is_empty()) |
| 157 return; |
| 158 |
| 159 size_t width = frame->size().width(); |
| 160 size_t height = frame->size().height(); |
| 161 if (!yuv_frame_ || yuv_frame_->GetWidth() != width || |
| 162 yuv_frame_->GetHeight() != height) { |
| 163 scoped_ptr<cricket::WebRtcVideoFrame> webrtc_frame( |
| 164 new cricket::WebRtcVideoFrame()); |
| 165 webrtc_frame->InitToEmptyBuffer(width, height, 1, 1, 0); |
| 166 yuv_frame_ = std::move(webrtc_frame); |
| 167 |
| 168 // Set updated_region so the whole frame is converted to YUV below. |
| 169 frame->mutable_updated_region()->SetRect( |
| 170 webrtc::DesktopRect::MakeWH(width, height)); |
| 171 } |
| 172 |
| 173 // TODO(sergeyu): This will copy the buffer if it's being used. Optimize it by |
| 174 // keeping a queue of frames. |
| 175 CHECK(yuv_frame_->MakeExclusive()); |
| 176 |
| 177 yuv_frame_->SetTimeStamp(base::TimeTicks::Now().ToInternalValue() * |
| 178 base::Time::kNanosecondsPerMicrosecond); |
| 179 |
| 180 for (webrtc::DesktopRegion::Iterator i(frame->updated_region()); !i.IsAtEnd(); |
| 181 i.Advance()) { |
| 182 int left = i.rect().left(); |
| 183 int top = i.rect().top(); |
| 184 int width = i.rect().width(); |
| 185 int height = i.rect().height(); |
| 186 |
| 187 if (left % 2 == 1) { |
| 188 --left; |
| 189 ++width; |
| 190 } |
| 191 if (top % 2 == 1) { |
| 192 --top; |
| 193 ++height; |
| 194 } |
| 195 libyuv::ARGBToI420( |
| 196 frame->data() + frame->stride() * top + |
| 197 left * webrtc::DesktopFrame::kBytesPerPixel, |
| 198 frame->stride(), |
| 199 yuv_frame_->GetYPlane() + yuv_frame_->GetYPitch() * top + left, |
| 200 yuv_frame_->GetYPitch(), |
| 201 yuv_frame_->GetUPlane() + yuv_frame_->GetUPitch() * top / 2 + left / 2, |
| 202 yuv_frame_->GetUPitch(), |
| 203 yuv_frame_->GetVPlane() + yuv_frame_->GetVPitch() * top / 2 + left / 2, |
| 204 yuv_frame_->GetVPitch(), width, height); |
| 205 } |
| 206 |
| 207 SignalVideoFrame(this, yuv_frame_.get()); |
| 188 } | 208 } |
| 189 | 209 |
| 190 void WebrtcVideoCapturerAdapter::CaptureNextFrame() { | 210 void WebrtcVideoCapturerAdapter::CaptureNextFrame() { |
| 191 // If we are paused, then don't capture. | 211 DCHECK(thread_checker_.CalledOnValidThread()); |
| 192 if (!IsRunning()) | 212 |
| 213 if (capture_pending_) |
| 193 return; | 214 return; |
| 194 | 215 capture_pending_ = true; |
| 195 desktop_capturer_->Capture(webrtc::DesktopRegion()); | 216 desktop_capturer_->Capture(webrtc::DesktopRegion()); |
| 196 } | 217 } |
| 197 | 218 |
| 219 } // namespace protocol |
| 198 } // namespace remoting | 220 } // namespace remoting |
| OLD | NEW |