1// Copyright (c) 2012 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/rtc_video_capturer.h"
6
7#include "base/bind.h"
8#include "base/debug/trace_event.h"
9
10namespace content {
11
12RtcVideoCapturer::RtcVideoCapturer(
13    const media::VideoCaptureSessionId id,
14    VideoCaptureImplManager* vc_manager,
15    bool is_screencast)
16    : is_screencast_(is_screencast),
17      delegate_(new RtcVideoCaptureDelegate(id, vc_manager)),
18      state_(VIDEO_CAPTURE_STATE_STOPPED) {
19}
20
21RtcVideoCapturer::~RtcVideoCapturer() {
22  DCHECK(VIDEO_CAPTURE_STATE_STOPPED);
23  DVLOG(3) << " RtcVideoCapturer::dtor";
24}
25
26cricket::CaptureState RtcVideoCapturer::Start(
27    const cricket::VideoFormat& capture_format) {
28  DVLOG(3) << " RtcVideoCapturer::Start ";
29  if (state_ == VIDEO_CAPTURE_STATE_STARTED) {
30    DVLOG(1) << "Got a StartCapture when already started!!! ";
31    return cricket::CS_FAILED;
32  }
33
34  media::VideoCaptureCapability cap;
35  cap.width = capture_format.width;
36  cap.height = capture_format.height;
37  cap.frame_rate = capture_format.framerate();
38  cap.color = media::VideoCaptureCapability::kI420;
39
40  SetCaptureFormat(&capture_format);
41
42  state_ = VIDEO_CAPTURE_STATE_STARTED;
43  start_time_ = base::Time::Now();
44  delegate_->StartCapture(cap,
45      base::Bind(&RtcVideoCapturer::OnFrameCaptured, base::Unretained(this)),
46      base::Bind(&RtcVideoCapturer::OnStateChange, base::Unretained(this)));
47  // Update the desired aspect ratio so that later the video frame can be
48  // cropped to meet the requirement if the camera returns a different
49  // resolution than the |cap|.
50  UpdateAspectRatio(cap.width, cap.height);
51  return cricket::CS_STARTING;
52}
53
54void RtcVideoCapturer::Stop() {
55  DVLOG(3) << " RtcVideoCapturer::Stop ";
56  if (state_ == VIDEO_CAPTURE_STATE_STOPPED) {
57    DVLOG(1) << "Got a StopCapture while not started.";
58    return;
59  }
60
61  SetCaptureFormat(NULL);
62  state_ = VIDEO_CAPTURE_STATE_STOPPED;
63  delegate_->StopCapture();
64  SignalStateChange(this, cricket::CS_STOPPED);
65}
66
67bool RtcVideoCapturer::IsRunning() {
68  return state_ == VIDEO_CAPTURE_STATE_STARTED;
69}
70
71bool RtcVideoCapturer::GetPreferredFourccs(std::vector<uint32>* fourccs) {
72  if (!fourccs)
73    return false;
74  fourccs->push_back(cricket::FOURCC_I420);
75  return true;
76}
77
78bool RtcVideoCapturer::IsScreencast() const {
79  return is_screencast_;
80}
81
82bool RtcVideoCapturer::GetBestCaptureFormat(const cricket::VideoFormat& desired,
83                                            cricket::VideoFormat* best_format) {
84  if (!best_format) {
85    return false;
86  }
87
88  // Chrome does not support capability enumeration.
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 RtcVideoCapturer::OnFrameCaptured(
98    const media::VideoCapture::VideoFrameBuffer& buf) {
99  // Currently, |fourcc| is always I420.
100  cricket::CapturedFrame frame;
101  frame.width = buf.width;
102  frame.height = buf.height;
103  frame.fourcc = cricket::FOURCC_I420;
104  frame.data_size = buf.buffer_size;
105  // cricket::CapturedFrame time is in nanoseconds.
106  frame.elapsed_time = (buf.timestamp - start_time_).InMicroseconds() *
107      base::Time::kNanosecondsPerMicrosecond;
108  frame.time_stamp =
109      (buf.timestamp - base::Time::UnixEpoch()).InMicroseconds() *
110      base::Time::kNanosecondsPerMicrosecond;
111  frame.data = buf.memory_pointer;
112  frame.pixel_height = 1;
113  frame.pixel_width = 1;
114
115  TRACE_EVENT_INSTANT2("rtc_video_capturer",
116                       "OnFrameCaptured",
117                       TRACE_EVENT_SCOPE_THREAD,
118                       "elapsed time",
119                       frame.elapsed_time,
120                       "timestamp_ms",
121                       frame.time_stamp / talk_base::kNumNanosecsPerMillisec);
122
123  // This signals to libJingle that a new VideoFrame is available.
124  // libJingle have no assumptions on what thread this signal come from.
125  SignalFrameCaptured(this, &frame);
126}
127
128void RtcVideoCapturer::OnStateChange(
129    RtcVideoCaptureDelegate::CaptureState state) {
130  cricket::CaptureState converted_state = cricket::CS_FAILED;
131  DVLOG(3) << " RtcVideoCapturer::OnStateChange " << state;
132  switch (state) {
133    case RtcVideoCaptureDelegate::CAPTURE_STOPPED:
134      converted_state = cricket::CS_STOPPED;
135      break;
136    case RtcVideoCaptureDelegate::CAPTURE_RUNNING:
137      converted_state = cricket::CS_RUNNING;
138      break;
139    case RtcVideoCaptureDelegate::CAPTURE_FAILED:
140      // TODO(perkj): Update the comments in the the definition of
141      // cricket::CS_FAILED. According to the comments, cricket::CS_FAILED
142      // means that the capturer failed to start. But here and in libjingle it
143      // is also used if an error occur during capturing.
144      converted_state = cricket::CS_FAILED;
145      break;
146    default:
147      NOTREACHED();
148      break;
149  }
150  SignalStateChange(this, converted_state);
151}
152
153}  // namespace content
154