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