// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "content/renderer/media/rtc_video_capturer.h" #include "base/bind.h" #include "base/debug/trace_event.h" #include "media/base/video_frame.h" namespace content { RtcVideoCapturer::RtcVideoCapturer(const media::VideoCaptureSessionId id, VideoCaptureImplManager* vc_manager, bool is_screencast) : is_screencast_(is_screencast), delegate_(new RtcVideoCaptureDelegate(id, vc_manager)), state_(VIDEO_CAPTURE_STATE_STOPPED) {} RtcVideoCapturer::~RtcVideoCapturer() { DCHECK(VIDEO_CAPTURE_STATE_STOPPED); DVLOG(3) << " RtcVideoCapturer::dtor"; } cricket::CaptureState RtcVideoCapturer::Start( const cricket::VideoFormat& capture_format) { DVLOG(3) << " RtcVideoCapturer::Start "; if (state_ == VIDEO_CAPTURE_STATE_STARTED) { DVLOG(1) << "Got a StartCapture when already started!!! "; return cricket::CS_FAILED; } media::VideoCaptureParams request; request.requested_format = media::VideoCaptureFormat( gfx::Size(capture_format.width, capture_format.height), capture_format.framerate(), media::PIXEL_FORMAT_I420); SetCaptureFormat(&capture_format); state_ = VIDEO_CAPTURE_STATE_STARTED; first_frame_timestamp_ = media::kNoTimestamp(); delegate_->StartCapture( request, base::Bind(&RtcVideoCapturer::OnFrameCaptured, base::Unretained(this)), base::Bind(&RtcVideoCapturer::OnStateChange, base::Unretained(this))); // Update the desired aspect ratio so that later the video frame can be // cropped to meet the requirement if the camera returns a different // resolution than the |request|. UpdateAspectRatio(capture_format.width, capture_format.height); return cricket::CS_STARTING; } void RtcVideoCapturer::Stop() { DVLOG(3) << " RtcVideoCapturer::Stop "; if (state_ == VIDEO_CAPTURE_STATE_STOPPED) { DVLOG(1) << "Got a StopCapture while not started."; return; } SetCaptureFormat(NULL); state_ = VIDEO_CAPTURE_STATE_STOPPED; delegate_->StopCapture(); SignalStateChange(this, cricket::CS_STOPPED); } bool RtcVideoCapturer::IsRunning() { return state_ == VIDEO_CAPTURE_STATE_STARTED; } bool RtcVideoCapturer::GetPreferredFourccs(std::vector<uint32>* fourccs) { if (!fourccs) return false; fourccs->push_back(cricket::FOURCC_I420); return true; } bool RtcVideoCapturer::IsScreencast() const { return is_screencast_; } bool RtcVideoCapturer::GetBestCaptureFormat(const cricket::VideoFormat& desired, cricket::VideoFormat* best_format) { if (!best_format) { return false; } // Chrome does not support capability enumeration. // Use the desired format as the best format. best_format->width = desired.width; best_format->height = desired.height; best_format->fourcc = cricket::FOURCC_I420; best_format->interval = desired.interval; return true; } void RtcVideoCapturer::OnFrameCaptured( const scoped_refptr<media::VideoFrame>& frame) { if (first_frame_timestamp_ == media::kNoTimestamp()) first_frame_timestamp_ = frame->GetTimestamp(); // Currently, |fourcc| is always I420. cricket::CapturedFrame captured_frame; captured_frame.width = frame->coded_size().width(); captured_frame.height = frame->coded_size().height(); captured_frame.fourcc = cricket::FOURCC_I420; // cricket::CapturedFrame time is in nanoseconds. captured_frame.elapsed_time = (frame->GetTimestamp() - first_frame_timestamp_).InMicroseconds() * base::Time::kNanosecondsPerMicrosecond; captured_frame.time_stamp = frame->GetTimestamp().InMicroseconds() * base::Time::kNanosecondsPerMicrosecond; // TODO(sheu): we assume contiguous layout of image planes. captured_frame.data = frame->data(0); captured_frame.data_size = media::VideoFrame::AllocationSize(frame->format(), frame->coded_size()); captured_frame.pixel_height = 1; captured_frame.pixel_width = 1; TRACE_EVENT_INSTANT2( "rtc_video_capturer", "OnFrameCaptured", TRACE_EVENT_SCOPE_THREAD, "elapsed time", captured_frame.elapsed_time, "timestamp_ms", captured_frame.time_stamp / talk_base::kNumNanosecsPerMillisec); // This signals to libJingle that a new VideoFrame is available. // libJingle have no assumptions on what thread this signal come from. SignalFrameCaptured(this, &captured_frame); } void RtcVideoCapturer::OnStateChange( RtcVideoCaptureDelegate::CaptureState state) { cricket::CaptureState converted_state = cricket::CS_FAILED; DVLOG(3) << " RtcVideoCapturer::OnStateChange " << state; switch (state) { case RtcVideoCaptureDelegate::CAPTURE_STOPPED: converted_state = cricket::CS_STOPPED; break; case RtcVideoCaptureDelegate::CAPTURE_RUNNING: converted_state = cricket::CS_RUNNING; break; case RtcVideoCaptureDelegate::CAPTURE_FAILED: // TODO(perkj): Update the comments in the the definition of // cricket::CS_FAILED. According to the comments, cricket::CS_FAILED // means that the capturer failed to start. But here and in libjingle it // is also used if an error occur during capturing. converted_state = cricket::CS_FAILED; break; default: NOTREACHED(); break; } SignalStateChange(this, converted_state); } } // namespace content