webrtc_video_capturer_adapter.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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/convert.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}
21
22WebRtcVideoCapturerAdapter::~WebRtcVideoCapturerAdapter() {
23  DVLOG(3) << " WebRtcVideoCapturerAdapter::dtor";
24  base::AlignedFree(buffer_);
25}
26
27void WebRtcVideoCapturerAdapter::SetRequestedFormat(
28    const media::VideoCaptureFormat& format) {
29  DCHECK_EQ(media::PIXEL_FORMAT_I420, format.pixel_format);
30  DVLOG(3) << "WebRtcVideoCapturerAdapter::SetRequestedFormat"
31           << " w = " << format.frame_size.width()
32           << " h = " << format.frame_size.height();
33  cricket::VideoFormat supported_format(format.frame_size.width(),
34                                        format.frame_size.height(),
35                                        cricket::VideoFormat::FpsToInterval(
36                                            format.frame_rate),
37                                        cricket::FOURCC_I420);
38  SetCaptureFormat(&supported_format);
39
40  // Update the desired aspect ratio so that later the video frame can be
41  // cropped to meet the requirement if the camera returns a different
42  // resolution than the |request|.
43  UpdateAspectRatio(format.frame_size.width(), format.frame_size.height());
44}
45
46cricket::CaptureState WebRtcVideoCapturerAdapter::Start(
47    const cricket::VideoFormat& capture_format) {
48  DCHECK(!running_);
49  DVLOG(3) << " WebRtcVideoCapturerAdapter::Start w = " << capture_format.width
50           << " h = " << capture_format.height;
51
52  running_ = true;
53  return cricket::CS_RUNNING;
54}
55
56void WebRtcVideoCapturerAdapter::Stop() {
57  DVLOG(3) << " WebRtcVideoCapturerAdapter::Stop ";
58  DCHECK(running_);
59  running_ = false;
60  SetCaptureFormat(NULL);
61  SignalStateChange(this, cricket::CS_STOPPED);
62}
63
64bool WebRtcVideoCapturerAdapter::IsRunning() {
65  return running_;
66}
67
68bool WebRtcVideoCapturerAdapter::GetPreferredFourccs(
69    std::vector<uint32>* fourccs) {
70  if (!fourccs)
71    return false;
72  fourccs->push_back(cricket::FOURCC_I420);
73  return true;
74}
75
76bool WebRtcVideoCapturerAdapter::IsScreencast() const {
77  return is_screencast_;
78}
79
80bool WebRtcVideoCapturerAdapter::GetBestCaptureFormat(
81    const cricket::VideoFormat& desired,
82    cricket::VideoFormat* best_format) {
83  DVLOG(3) << " GetBestCaptureFormat:: "
84           << " w = " << desired.width
85           << " h = " << desired.height;
86
87  // Capability enumeration is done in MediaStreamVideoSource. The adapter can
88  // just use what is provided.
89  // Use the desired format as the best format.
90  best_format->width = desired.width;
91  best_format->height = desired.height;
92  best_format->fourcc = cricket::FOURCC_I420;
93  best_format->interval = desired.interval;
94  return true;
95}
96
97void WebRtcVideoCapturerAdapter::OnFrameCaptured(
98    const scoped_refptr<media::VideoFrame>& frame) {
99  DCHECK(media::VideoFrame::I420 == frame->format() ||
100         media::VideoFrame::YV12 == frame->format());
101  if (first_frame_timestamp_ == media::kNoTimestamp())
102    first_frame_timestamp_ = frame->GetTimestamp();
103
104  cricket::CapturedFrame captured_frame;
105  captured_frame.width = frame->visible_rect().width();
106  captured_frame.height = frame->visible_rect().height();
107  // cricket::CapturedFrame time is in nanoseconds.
108  captured_frame.elapsed_time =
109      (frame->GetTimestamp() - first_frame_timestamp_).InMicroseconds() *
110      base::Time::kNanosecondsPerMicrosecond;
111  captured_frame.time_stamp = frame->GetTimestamp().InMicroseconds() *
112                              base::Time::kNanosecondsPerMicrosecond;
113  captured_frame.pixel_height = 1;
114  captured_frame.pixel_width = 1;
115
116  // TODO(perkj):
117  // Libjingle expects contiguous layout of image planes as input.
118  // The only format where that is true in Chrome is I420 where the
119  // coded_size == visible_rect().size().
120  if (frame->format() != media::VideoFrame::I420 ||
121      frame->coded_size() != frame->visible_rect().size()) {
122    // Cropping and or switching UV planes is needed.
123    UpdateI420Buffer(frame);
124    captured_frame.data = buffer_;
125    captured_frame.data_size = buffer_size_;
126    captured_frame.fourcc = cricket::FOURCC_I420;
127  } else {
128    captured_frame.fourcc = media::VideoFrame::I420 == frame->format() ?
129        cricket::FOURCC_I420 : cricket::FOURCC_YV12;
130    captured_frame.data = frame->data(0);
131    captured_frame.data_size =
132        media::VideoFrame::AllocationSize(frame->format(), frame->coded_size());
133  }
134
135  // This signals to libJingle that a new VideoFrame is available.
136  // libJingle have no assumptions on what thread this signal come from.
137  SignalFrameCaptured(this, &captured_frame);
138}
139
140void WebRtcVideoCapturerAdapter::UpdateI420Buffer(
141    const scoped_refptr<media::VideoFrame>& src) {
142  const int src_width = src->coded_size().width();
143  const int src_height = src->coded_size().height();
144  const int dst_width = src->visible_rect().width();
145  const int dst_height = src->visible_rect().height();
146  DCHECK(src_width >= dst_width && src_height >= dst_height);
147
148  const int horiz_crop = src->visible_rect().x();
149  const int vert_crop = src->visible_rect().y();
150
151  const uint8* src_y = src->data(media::VideoFrame::kYPlane) +
152      (src_width * vert_crop + horiz_crop);
153  const int center = (src_width + 1) / 2;
154  const uint8* src_u = src->data(media::VideoFrame::kUPlane) +
155      (center * vert_crop + horiz_crop) / 2;
156  const uint8* src_v = src->data(media::VideoFrame::kVPlane) +
157      (center * vert_crop + horiz_crop) / 2;
158
159  const size_t dst_size =
160      media::VideoFrame::AllocationSize(src->format(),
161                                        src->visible_rect().size());
162
163  if (dst_size != buffer_size_) {
164    base::AlignedFree(buffer_);
165    buffer_ = reinterpret_cast<uint8*>(
166        base::AlignedAlloc(dst_size + media::VideoFrame::kFrameSizePadding,
167                           media::VideoFrame::kFrameAddressAlignment));
168    buffer_size_ = dst_size;
169  }
170
171  uint8* dst_y = buffer_;
172  const int dst_stride_y = dst_width;
173  uint8* dst_u = dst_y + dst_width * dst_height;
174  const int dst_halfwidth = (dst_width + 1) / 2;
175  const int dst_halfheight = (dst_height + 1) / 2;
176  uint8* dst_v = dst_u + dst_halfwidth * dst_halfheight;
177
178  libyuv::I420Copy(src_y,
179                   src->stride(media::VideoFrame::kYPlane),
180                   src_u,
181                   src->stride(media::VideoFrame::kUPlane),
182                   src_v,
183                   src->stride(media::VideoFrame::kVPlane),
184                   dst_y,
185                   dst_stride_y,
186                   dst_u,
187                   dst_halfwidth,
188                   dst_v,
189                   dst_halfwidth,
190                   dst_width,
191                   dst_height);
192}
193
194}  // namespace content
195