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/media_stream_video_capturer_source.h"
6
7 #include "base/bind.h"
8 #include "base/callback_helpers.h"
9 #include "base/location.h"
10 #include "content/renderer/media/video_capture_impl_manager.h"
11 #include "content/renderer/render_thread_impl.h"
12 #include "media/base/bind_to_current_loop.h"
13 #include "media/base/video_frame.h"
14
15 namespace {
16
17 struct SourceVideoResolution {
18 int width;
19 int height;
20 };
21
22 // Resolutions used if the source doesn't support capability enumeration.
23 const SourceVideoResolution kVideoResolutions[] = {{1920, 1080},
24 {1280, 720},
25 {960, 720},
26 {640, 480},
27 {640, 360},
28 {320, 240},
29 {320, 180}};
30 // Frame rates for sources with no support for capability enumeration.
31 const int kVideoFrameRates[] = {30, 60};
32
33 // Hard upper-bound frame rate for tab/desktop capture.
34 const double kMaxScreenCastFrameRate = 120.0;
35
36 } // namespace
37
38 namespace content {
39
VideoCapturerDelegate(const StreamDeviceInfo & device_info)40 VideoCapturerDelegate::VideoCapturerDelegate(
41 const StreamDeviceInfo& device_info)
42 : session_id_(device_info.session_id),
43 is_screen_cast_(device_info.device.type == MEDIA_TAB_VIDEO_CAPTURE ||
44 device_info.device.type == MEDIA_DESKTOP_VIDEO_CAPTURE) {
45 DVLOG(3) << "VideoCapturerDelegate::ctor";
46
47 // NULL in unit test.
48 if (RenderThreadImpl::current()) {
49 VideoCaptureImplManager* manager =
50 RenderThreadImpl::current()->video_capture_impl_manager();
51 if (manager)
52 release_device_cb_ = manager->UseDevice(session_id_);
53 }
54 }
55
~VideoCapturerDelegate()56 VideoCapturerDelegate::~VideoCapturerDelegate() {
57 DVLOG(3) << "VideoCapturerDelegate::dtor";
58 if (!release_device_cb_.is_null())
59 release_device_cb_.Run();
60 }
61
GetCurrentSupportedFormats(int max_requested_width,int max_requested_height,double max_requested_frame_rate,const VideoCaptureDeviceFormatsCB & callback)62 void VideoCapturerDelegate::GetCurrentSupportedFormats(
63 int max_requested_width,
64 int max_requested_height,
65 double max_requested_frame_rate,
66 const VideoCaptureDeviceFormatsCB& callback) {
67 DVLOG(3)
68 << "GetCurrentSupportedFormats("
69 << " { max_requested_height = " << max_requested_height << "})"
70 << " { max_requested_width = " << max_requested_width << "})"
71 << " { max_requested_frame_rate = " << max_requested_frame_rate << "})";
72
73 if (is_screen_cast_) {
74 const int width = max_requested_width ?
75 max_requested_width : MediaStreamVideoSource::kDefaultWidth;
76 const int height = max_requested_height ?
77 max_requested_height : MediaStreamVideoSource::kDefaultHeight;
78 callback.Run(media::VideoCaptureFormats(1, media::VideoCaptureFormat(
79 gfx::Size(width, height),
80 static_cast<float>(std::min(kMaxScreenCastFrameRate,
81 max_requested_frame_rate)),
82 media::PIXEL_FORMAT_I420)));
83 return;
84 }
85
86 // NULL in unit test.
87 if (!RenderThreadImpl::current())
88 return;
89 VideoCaptureImplManager* manager =
90 RenderThreadImpl::current()->video_capture_impl_manager();
91 if (!manager)
92 return;
93 DCHECK(source_formats_callback_.is_null());
94 source_formats_callback_ = callback;
95 manager->GetDeviceFormatsInUse(
96 session_id_,
97 media::BindToCurrentLoop(
98 base::Bind(
99 &VideoCapturerDelegate::OnDeviceFormatsInUseReceived, this)));
100 }
101
StartCapture(const media::VideoCaptureParams & params,const VideoCaptureDeliverFrameCB & new_frame_callback,const RunningCallback & running_callback)102 void VideoCapturerDelegate::StartCapture(
103 const media::VideoCaptureParams& params,
104 const VideoCaptureDeliverFrameCB& new_frame_callback,
105 const RunningCallback& running_callback) {
106 DCHECK(params.requested_format.IsValid());
107 DCHECK(thread_checker_.CalledOnValidThread());
108 running_callback_ = running_callback;
109
110 // NULL in unit test.
111 if (!RenderThreadImpl::current())
112 return;
113 VideoCaptureImplManager* manager =
114 RenderThreadImpl::current()->video_capture_impl_manager();
115 if (!manager)
116 return;
117 stop_capture_cb_ =
118 manager->StartCapture(
119 session_id_,
120 params,
121 media::BindToCurrentLoop(base::Bind(
122 &VideoCapturerDelegate::OnStateUpdateOnRenderThread, this)),
123 new_frame_callback);
124 }
125
StopCapture()126 void VideoCapturerDelegate::StopCapture() {
127 // Immediately make sure we don't provide more frames.
128 DVLOG(3) << "VideoCapturerDelegate::StopCapture()";
129 DCHECK(thread_checker_.CalledOnValidThread());
130 if (!stop_capture_cb_.is_null()) {
131 base::ResetAndReturn(&stop_capture_cb_).Run();
132 }
133 running_callback_.Reset();
134 source_formats_callback_.Reset();
135 }
136
OnStateUpdateOnRenderThread(VideoCaptureState state)137 void VideoCapturerDelegate::OnStateUpdateOnRenderThread(
138 VideoCaptureState state) {
139 DCHECK(thread_checker_.CalledOnValidThread());
140 DVLOG(3) << "OnStateUpdateOnRenderThread state = " << state;
141 if (state == VIDEO_CAPTURE_STATE_STARTED && !running_callback_.is_null()) {
142 running_callback_.Run(MEDIA_DEVICE_OK);
143 return;
144 }
145 if (state > VIDEO_CAPTURE_STATE_STARTED && !running_callback_.is_null()) {
146 base::ResetAndReturn(&running_callback_).Run(
147 MEDIA_DEVICE_TRACK_START_FAILURE);
148 }
149 }
150
OnDeviceFormatsInUseReceived(const media::VideoCaptureFormats & formats_in_use)151 void VideoCapturerDelegate::OnDeviceFormatsInUseReceived(
152 const media::VideoCaptureFormats& formats_in_use) {
153 DVLOG(3) << "OnDeviceFormatsInUseReceived: " << formats_in_use.size();
154 DCHECK(thread_checker_.CalledOnValidThread());
155 // StopCapture() might have destroyed |source_formats_callback_| before
156 // arriving here.
157 if (source_formats_callback_.is_null())
158 return;
159 // If there are no formats in use, try to retrieve the whole list of
160 // supported form.
161 if (!formats_in_use.empty()) {
162 source_formats_callback_.Run(formats_in_use);
163 source_formats_callback_.Reset();
164 return;
165 }
166
167 // NULL in unit test.
168 if (!RenderThreadImpl::current())
169 return;
170 VideoCaptureImplManager* manager =
171 RenderThreadImpl::current()->video_capture_impl_manager();
172 if (!manager)
173 return;
174 manager->GetDeviceSupportedFormats(
175 session_id_,
176 media::BindToCurrentLoop(
177 base::Bind(
178 &VideoCapturerDelegate::OnDeviceSupportedFormatsEnumerated,
179 this)));
180 }
181
OnDeviceSupportedFormatsEnumerated(const media::VideoCaptureFormats & formats)182 void VideoCapturerDelegate::OnDeviceSupportedFormatsEnumerated(
183 const media::VideoCaptureFormats& formats) {
184 DVLOG(3) << "OnDeviceSupportedFormatsEnumerated: " << formats.size()
185 << " received";
186 DCHECK(thread_checker_.CalledOnValidThread());
187 // StopCapture() might have destroyed |source_formats_callback_| before
188 // arriving here.
189 if (source_formats_callback_.is_null())
190 return;
191 if (formats.size()) {
192 source_formats_callback_.Run(formats);
193 } else {
194 // The capture device doesn't seem to support capability enumeration,
195 // compose a fallback list of capabilities.
196 media::VideoCaptureFormats default_formats;
197 for (size_t i = 0; i < arraysize(kVideoResolutions); ++i) {
198 for (size_t j = 0; j < arraysize(kVideoFrameRates); ++j) {
199 default_formats.push_back(media::VideoCaptureFormat(
200 gfx::Size(kVideoResolutions[i].width, kVideoResolutions[i].height),
201 kVideoFrameRates[j], media::PIXEL_FORMAT_I420));
202 }
203 }
204 source_formats_callback_.Run(default_formats);
205 }
206 source_formats_callback_.Reset();
207 }
208
MediaStreamVideoCapturerSource(const StreamDeviceInfo & device_info,const SourceStoppedCallback & stop_callback,const scoped_refptr<VideoCapturerDelegate> & delegate)209 MediaStreamVideoCapturerSource::MediaStreamVideoCapturerSource(
210 const StreamDeviceInfo& device_info,
211 const SourceStoppedCallback& stop_callback,
212 const scoped_refptr<VideoCapturerDelegate>& delegate)
213 : delegate_(delegate) {
214 SetDeviceInfo(device_info);
215 SetStopCallback(stop_callback);
216 }
217
~MediaStreamVideoCapturerSource()218 MediaStreamVideoCapturerSource::~MediaStreamVideoCapturerSource() {
219 }
220
GetCurrentSupportedFormats(int max_requested_width,int max_requested_height,double max_requested_frame_rate,const VideoCaptureDeviceFormatsCB & callback)221 void MediaStreamVideoCapturerSource::GetCurrentSupportedFormats(
222 int max_requested_width,
223 int max_requested_height,
224 double max_requested_frame_rate,
225 const VideoCaptureDeviceFormatsCB& callback) {
226 delegate_->GetCurrentSupportedFormats(
227 max_requested_width,
228 max_requested_height,
229 max_requested_frame_rate,
230 callback);
231 }
232
StartSourceImpl(const media::VideoCaptureFormat & format,const VideoCaptureDeliverFrameCB & frame_callback)233 void MediaStreamVideoCapturerSource::StartSourceImpl(
234 const media::VideoCaptureFormat& format,
235 const VideoCaptureDeliverFrameCB& frame_callback) {
236 media::VideoCaptureParams new_params;
237 new_params.requested_format = format;
238 if (device_info().device.type == MEDIA_TAB_VIDEO_CAPTURE ||
239 device_info().device.type == MEDIA_DESKTOP_VIDEO_CAPTURE) {
240 new_params.resolution_change_policy =
241 media::RESOLUTION_POLICY_DYNAMIC_WITHIN_LIMIT;
242 }
243 delegate_->StartCapture(
244 new_params,
245 frame_callback,
246 base::Bind(&MediaStreamVideoCapturerSource::OnStartDone,
247 base::Unretained(this)));
248 }
249
StopSourceImpl()250 void MediaStreamVideoCapturerSource::StopSourceImpl() {
251 delegate_->StopCapture();
252 }
253
254 } // namespace content
255