1// Copyright 2014 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "content/renderer/media/webrtc/webrtc_video_capturer_adapter.h" 6 7#include "base/bind.h" 8#include "base/debug/trace_event.h" 9#include "base/memory/aligned_memory.h" 10#include "media/base/video_frame.h" 11#include "third_party/libyuv/include/libyuv/scale.h" 12 13namespace content { 14 15WebRtcVideoCapturerAdapter::WebRtcVideoCapturerAdapter(bool is_screencast) 16 : is_screencast_(is_screencast), 17 running_(false), 18 buffer_(NULL), 19 buffer_size_(0) { 20 thread_checker_.DetachFromThread(); 21} 22 23WebRtcVideoCapturerAdapter::~WebRtcVideoCapturerAdapter() { 24 DVLOG(3) << " WebRtcVideoCapturerAdapter::dtor"; 25 base::AlignedFree(buffer_); 26} 27 28cricket::CaptureState WebRtcVideoCapturerAdapter::Start( 29 const cricket::VideoFormat& capture_format) { 30 DCHECK(thread_checker_.CalledOnValidThread()); 31 DCHECK(!running_); 32 DVLOG(3) << " WebRtcVideoCapturerAdapter::Start w = " << capture_format.width 33 << " h = " << capture_format.height; 34 35 running_ = true; 36 return cricket::CS_RUNNING; 37} 38 39void WebRtcVideoCapturerAdapter::Stop() { 40 DCHECK(thread_checker_.CalledOnValidThread()); 41 DVLOG(3) << " WebRtcVideoCapturerAdapter::Stop "; 42 DCHECK(running_); 43 running_ = false; 44 SetCaptureFormat(NULL); 45 SignalStateChange(this, cricket::CS_STOPPED); 46} 47 48bool WebRtcVideoCapturerAdapter::IsRunning() { 49 DCHECK(thread_checker_.CalledOnValidThread()); 50 return running_; 51} 52 53bool WebRtcVideoCapturerAdapter::GetPreferredFourccs( 54 std::vector<uint32>* fourccs) { 55 DCHECK(thread_checker_.CalledOnValidThread()); 56 if (!fourccs) 57 return false; 58 fourccs->push_back(cricket::FOURCC_I420); 59 return true; 60} 61 62bool WebRtcVideoCapturerAdapter::IsScreencast() const { 63 return is_screencast_; 64} 65 66bool WebRtcVideoCapturerAdapter::GetBestCaptureFormat( 67 const cricket::VideoFormat& desired, 68 cricket::VideoFormat* best_format) { 69 DCHECK(thread_checker_.CalledOnValidThread()); 70 DVLOG(3) << " GetBestCaptureFormat:: " 71 << " w = " << desired.width 72 << " h = " << desired.height; 73 74 // Capability enumeration is done in MediaStreamVideoSource. The adapter can 75 // just use what is provided. 76 // Use the desired format as the best format. 77 best_format->width = desired.width; 78 best_format->height = desired.height; 79 best_format->fourcc = cricket::FOURCC_I420; 80 best_format->interval = desired.interval; 81 return true; 82} 83 84void WebRtcVideoCapturerAdapter::OnFrameCaptured( 85 const scoped_refptr<media::VideoFrame>& frame) { 86 DCHECK(thread_checker_.CalledOnValidThread()); 87 TRACE_EVENT0("video", "WebRtcVideoCapturerAdapter::OnFrameCaptured"); 88 if (!(media::VideoFrame::I420 == frame->format() || 89 media::VideoFrame::YV12 == frame->format())) { 90 // Some types of sources support textures as output. Since connecting 91 // sources and sinks do not check the format, we need to just ignore 92 // formats that we can not handle. 93 NOTREACHED(); 94 return; 95 } 96 97 if (first_frame_timestamp_ == media::kNoTimestamp()) 98 first_frame_timestamp_ = frame->timestamp(); 99 100 cricket::CapturedFrame captured_frame; 101 captured_frame.width = frame->natural_size().width(); 102 captured_frame.height = frame->natural_size().height(); 103 // cricket::CapturedFrame time is in nanoseconds. 104 captured_frame.elapsed_time = 105 (frame->timestamp() - first_frame_timestamp_).InMicroseconds() * 106 base::Time::kNanosecondsPerMicrosecond; 107 captured_frame.time_stamp = frame->timestamp().InMicroseconds() * 108 base::Time::kNanosecondsPerMicrosecond; 109 captured_frame.pixel_height = 1; 110 captured_frame.pixel_width = 1; 111 112 // TODO(perkj): 113 // Libjingle expects contiguous layout of image planes as input. 114 // The only format where that is true in Chrome is I420 where the 115 // coded_size == natural_size(). 116 if (frame->format() != media::VideoFrame::I420 || 117 frame->coded_size() != frame->natural_size()) { 118 // Cropping / Scaling and or switching UV planes is needed. 119 UpdateI420Buffer(frame); 120 captured_frame.data = buffer_; 121 captured_frame.data_size = buffer_size_; 122 captured_frame.fourcc = cricket::FOURCC_I420; 123 } else { 124 captured_frame.fourcc = media::VideoFrame::I420 == frame->format() ? 125 cricket::FOURCC_I420 : cricket::FOURCC_YV12; 126 captured_frame.data = frame->data(0); 127 captured_frame.data_size = 128 media::VideoFrame::AllocationSize(frame->format(), frame->coded_size()); 129 } 130 131 // This signals to libJingle that a new VideoFrame is available. 132 // libJingle have no assumptions on what thread this signal come from. 133 SignalFrameCaptured(this, &captured_frame); 134} 135 136void WebRtcVideoCapturerAdapter::UpdateI420Buffer( 137 const scoped_refptr<media::VideoFrame>& src) { 138 DCHECK(thread_checker_.CalledOnValidThread()); 139 const int dst_width = src->natural_size().width(); 140 const int dst_height = src->natural_size().height(); 141 DCHECK(src->visible_rect().width() >= dst_width && 142 src->visible_rect().height() >= dst_height); 143 144 const gfx::Rect& visible_rect = src->visible_rect(); 145 146 const uint8* src_y = src->data(media::VideoFrame::kYPlane) + 147 visible_rect.y() * src->stride(media::VideoFrame::kYPlane) + 148 visible_rect.x(); 149 const uint8* src_u = src->data(media::VideoFrame::kUPlane) + 150 visible_rect.y() / 2 * src->stride(media::VideoFrame::kUPlane) + 151 visible_rect.x() / 2; 152 const uint8* src_v = src->data(media::VideoFrame::kVPlane) + 153 visible_rect.y() / 2 * src->stride(media::VideoFrame::kVPlane) + 154 visible_rect.x() / 2; 155 156 const size_t dst_size = 157 media::VideoFrame::AllocationSize(src->format(), src->natural_size()); 158 159 if (dst_size != buffer_size_) { 160 base::AlignedFree(buffer_); 161 buffer_ = reinterpret_cast<uint8*>( 162 base::AlignedAlloc(dst_size + media::VideoFrame::kFrameSizePadding, 163 media::VideoFrame::kFrameAddressAlignment)); 164 buffer_size_ = dst_size; 165 } 166 167 uint8* dst_y = buffer_; 168 const int dst_stride_y = dst_width; 169 uint8* dst_u = dst_y + dst_width * dst_height; 170 const int dst_halfwidth = (dst_width + 1) / 2; 171 const int dst_halfheight = (dst_height + 1) / 2; 172 uint8* dst_v = dst_u + dst_halfwidth * dst_halfheight; 173 174 libyuv::I420Scale(src_y, 175 src->stride(media::VideoFrame::kYPlane), 176 src_u, 177 src->stride(media::VideoFrame::kUPlane), 178 src_v, 179 src->stride(media::VideoFrame::kVPlane), 180 visible_rect.width(), 181 visible_rect.height(), 182 dst_y, 183 dst_stride_y, 184 dst_u, 185 dst_halfwidth, 186 dst_v, 187 dst_halfwidth, 188 dst_width, 189 dst_height, 190 libyuv::kFilterBilinear); 191} 192 193} // namespace content 194