Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(688)

Side by Side Diff: webrtc/api/videosource.cc

Issue 1767373002: Remove implementation in videosource.cc (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright 2012 The WebRTC project authors. All Rights Reserved. 2 * Copyright 2012 The WebRTC project authors. All Rights Reserved.
3 * 3 *
4 * Use of this source code is governed by a BSD-style license 4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source 5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found 6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may 7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree. 8 * be found in the AUTHORS file in the root of the source tree.
9 */ 9 */
10 10
11 #include "webrtc/api/videosource.h" 11 // TODO(perkj): Remove this file once Chrome build files doesn't depend on it.
12
13 #include <cstdlib>
14 #include <string>
15 #include <vector>
16
17 #include "webrtc/api/mediaconstraintsinterface.h"
18 #include "webrtc/base/arraysize.h"
19
20 using cricket::CaptureState;
21 using webrtc::MediaConstraintsInterface;
22 using webrtc::MediaSourceInterface;
23
24 namespace {
25
26 const double kRoundingTruncation = 0.0005;
27
28 // Default resolution. If no constraint is specified, this is the resolution we
29 // will use.
30 static const cricket::VideoFormatPod kDefaultFormat =
31 {640, 480, FPS_TO_INTERVAL(30), cricket::FOURCC_ANY};
32
33 // List of formats used if the camera doesn't support capability enumeration.
34 static const cricket::VideoFormatPod kVideoFormats[] = {
35 {1920, 1080, FPS_TO_INTERVAL(30), cricket::FOURCC_ANY},
36 {1280, 720, FPS_TO_INTERVAL(30), cricket::FOURCC_ANY},
37 {960, 720, FPS_TO_INTERVAL(30), cricket::FOURCC_ANY},
38 {640, 360, FPS_TO_INTERVAL(30), cricket::FOURCC_ANY},
39 {640, 480, FPS_TO_INTERVAL(30), cricket::FOURCC_ANY},
40 {320, 240, FPS_TO_INTERVAL(30), cricket::FOURCC_ANY},
41 {320, 180, FPS_TO_INTERVAL(30), cricket::FOURCC_ANY}
42 };
43
44 MediaSourceInterface::SourceState
45 GetReadyState(cricket::CaptureState state) {
46 switch (state) {
47 case cricket::CS_STARTING:
48 return MediaSourceInterface::kInitializing;
49 case cricket::CS_RUNNING:
50 return MediaSourceInterface::kLive;
51 case cricket::CS_FAILED:
52 case cricket::CS_STOPPED:
53 return MediaSourceInterface::kEnded;
54 case cricket::CS_PAUSED:
55 return MediaSourceInterface::kMuted;
56 default:
57 ASSERT(false && "GetReadyState unknown state");
58 }
59 return MediaSourceInterface::kEnded;
60 }
61
62 void SetUpperLimit(int new_limit, int* original_limit) {
63 if (*original_limit < 0 || new_limit < *original_limit)
64 *original_limit = new_limit;
65 }
66
67 // Updates |format_upper_limit| from |constraint|.
68 // If constraint.maxFoo is smaller than format_upper_limit.foo,
69 // set format_upper_limit.foo to constraint.maxFoo.
70 void SetUpperLimitFromConstraint(
71 const MediaConstraintsInterface::Constraint& constraint,
72 cricket::VideoFormat* format_upper_limit) {
73 if (constraint.key == MediaConstraintsInterface::kMaxWidth) {
74 int value = rtc::FromString<int>(constraint.value);
75 SetUpperLimit(value, &(format_upper_limit->width));
76 } else if (constraint.key == MediaConstraintsInterface::kMaxHeight) {
77 int value = rtc::FromString<int>(constraint.value);
78 SetUpperLimit(value, &(format_upper_limit->height));
79 }
80 }
81
82 // Fills |format_out| with the max width and height allowed by |constraints|.
83 void FromConstraintsForScreencast(
84 const MediaConstraintsInterface::Constraints& constraints,
85 cricket::VideoFormat* format_out) {
86 typedef MediaConstraintsInterface::Constraints::const_iterator
87 ConstraintsIterator;
88
89 cricket::VideoFormat upper_limit(-1, -1, 0, 0);
90 for (ConstraintsIterator constraints_it = constraints.begin();
91 constraints_it != constraints.end(); ++constraints_it)
92 SetUpperLimitFromConstraint(*constraints_it, &upper_limit);
93
94 if (upper_limit.width >= 0)
95 format_out->width = upper_limit.width;
96 if (upper_limit.height >= 0)
97 format_out->height = upper_limit.height;
98 }
99
100 // Returns true if |constraint| is fulfilled. |format_out| can differ from
101 // |format_in| if the format is changed by the constraint. Ie - the frame rate
102 // can be changed by setting maxFrameRate.
103 bool NewFormatWithConstraints(
104 const MediaConstraintsInterface::Constraint& constraint,
105 const cricket::VideoFormat& format_in,
106 bool mandatory,
107 cricket::VideoFormat* format_out) {
108 ASSERT(format_out != NULL);
109 *format_out = format_in;
110
111 if (constraint.key == MediaConstraintsInterface::kMinWidth) {
112 int value = rtc::FromString<int>(constraint.value);
113 return (value <= format_in.width);
114 } else if (constraint.key == MediaConstraintsInterface::kMaxWidth) {
115 int value = rtc::FromString<int>(constraint.value);
116 return (value >= format_in.width);
117 } else if (constraint.key == MediaConstraintsInterface::kMinHeight) {
118 int value = rtc::FromString<int>(constraint.value);
119 return (value <= format_in.height);
120 } else if (constraint.key == MediaConstraintsInterface::kMaxHeight) {
121 int value = rtc::FromString<int>(constraint.value);
122 return (value >= format_in.height);
123 } else if (constraint.key == MediaConstraintsInterface::kMinFrameRate) {
124 int value = rtc::FromString<int>(constraint.value);
125 return (value <= cricket::VideoFormat::IntervalToFps(format_in.interval));
126 } else if (constraint.key == MediaConstraintsInterface::kMaxFrameRate) {
127 int value = rtc::FromString<int>(constraint.value);
128 if (value == 0) {
129 if (mandatory) {
130 // TODO(ronghuawu): Convert the constraint value to float when sub-1fps
131 // is supported by the capturer.
132 return false;
133 } else {
134 value = 1;
135 }
136 }
137 if (value <= cricket::VideoFormat::IntervalToFps(format_in.interval))
138 format_out->interval = cricket::VideoFormat::FpsToInterval(value);
139 return true;
140 } else if (constraint.key == MediaConstraintsInterface::kMinAspectRatio) {
141 double value = rtc::FromString<double>(constraint.value);
142 // The aspect ratio in |constraint.value| has been converted to a string and
143 // back to a double, so it may have a rounding error.
144 // E.g if the value 1/3 is converted to a string, the string will not have
145 // infinite length.
146 // We add a margin of 0.0005 which is high enough to detect the same aspect
147 // ratio but small enough to avoid matching wrong aspect ratios.
148 double ratio = static_cast<double>(format_in.width) / format_in.height;
149 return (value <= ratio + kRoundingTruncation);
150 } else if (constraint.key == MediaConstraintsInterface::kMaxAspectRatio) {
151 double value = rtc::FromString<double>(constraint.value);
152 double ratio = static_cast<double>(format_in.width) / format_in.height;
153 // Subtract 0.0005 to avoid rounding problems. Same as above.
154 const double kRoundingTruncation = 0.0005;
155 return (value >= ratio - kRoundingTruncation);
156 } else if (constraint.key == MediaConstraintsInterface::kNoiseReduction) {
157 // These are actually options, not constraints, so they can be satisfied
158 // regardless of the format.
159 return true;
160 }
161 LOG(LS_WARNING) << "Found unknown MediaStream constraint. Name:"
162 << constraint.key << " Value:" << constraint.value;
163 return false;
164 }
165
166 // Removes cricket::VideoFormats from |formats| that don't meet |constraint|.
167 void FilterFormatsByConstraint(
168 const MediaConstraintsInterface::Constraint& constraint,
169 bool mandatory,
170 std::vector<cricket::VideoFormat>* formats) {
171 std::vector<cricket::VideoFormat>::iterator format_it =
172 formats->begin();
173 while (format_it != formats->end()) {
174 // Modify the format_it to fulfill the constraint if possible.
175 // Delete it otherwise.
176 if (!NewFormatWithConstraints(constraint, (*format_it),
177 mandatory, &(*format_it))) {
178 format_it = formats->erase(format_it);
179 } else {
180 ++format_it;
181 }
182 }
183 }
184
185 // Returns a vector of cricket::VideoFormat that best match |constraints|.
186 std::vector<cricket::VideoFormat> FilterFormats(
187 const MediaConstraintsInterface::Constraints& mandatory,
188 const MediaConstraintsInterface::Constraints& optional,
189 const std::vector<cricket::VideoFormat>& supported_formats) {
190 typedef MediaConstraintsInterface::Constraints::const_iterator
191 ConstraintsIterator;
192 std::vector<cricket::VideoFormat> candidates = supported_formats;
193
194 for (ConstraintsIterator constraints_it = mandatory.begin();
195 constraints_it != mandatory.end(); ++constraints_it)
196 FilterFormatsByConstraint(*constraints_it, true, &candidates);
197
198 if (candidates.size() == 0)
199 return candidates;
200
201 // Ok - all mandatory checked and we still have a candidate.
202 // Let's try filtering using the optional constraints.
203 for (ConstraintsIterator constraints_it = optional.begin();
204 constraints_it != optional.end(); ++constraints_it) {
205 std::vector<cricket::VideoFormat> current_candidates = candidates;
206 FilterFormatsByConstraint(*constraints_it, false, &current_candidates);
207 if (current_candidates.size() > 0) {
208 candidates = current_candidates;
209 }
210 }
211
212 // We have done as good as we can to filter the supported resolutions.
213 return candidates;
214 }
215
216 // Find the format that best matches the default video size.
217 // Constraints are optional and since the performance of a video call
218 // might be bad due to bitrate limitations, CPU, and camera performance,
219 // it is better to select a resolution that is as close as possible to our
220 // default and still meets the contraints.
221 const cricket::VideoFormat& GetBestCaptureFormat(
222 const std::vector<cricket::VideoFormat>& formats) {
223 ASSERT(formats.size() > 0);
224
225 int default_area = kDefaultFormat.width * kDefaultFormat.height;
226
227 std::vector<cricket::VideoFormat>::const_iterator it = formats.begin();
228 std::vector<cricket::VideoFormat>::const_iterator best_it = formats.begin();
229 int best_diff_area = std::abs(default_area - it->width * it->height);
230 int64_t best_diff_interval = kDefaultFormat.interval;
231 for (; it != formats.end(); ++it) {
232 int diff_area = std::abs(default_area - it->width * it->height);
233 int64_t diff_interval = std::abs(kDefaultFormat.interval - it->interval);
234 if (diff_area < best_diff_area ||
235 (diff_area == best_diff_area && diff_interval < best_diff_interval)) {
236 best_diff_area = diff_area;
237 best_diff_interval = diff_interval;
238 best_it = it;
239 }
240 }
241 return *best_it;
242 }
243
244 // Set |option| to the highest-priority value of |key| in the constraints.
245 // Return false if the key is mandatory, and the value is invalid.
246 bool ExtractOption(const MediaConstraintsInterface* all_constraints,
247 const std::string& key,
248 rtc::Optional<bool>* option) {
249 size_t mandatory = 0;
250 bool value;
251 if (FindConstraint(all_constraints, key, &value, &mandatory)) {
252 *option = rtc::Optional<bool>(value);
253 return true;
254 }
255
256 return mandatory == 0;
257 }
258
259 // Search |all_constraints| for known video options. Apply all options that are
260 // found with valid values, and return false if any mandatory video option was
261 // found with an invalid value.
262 bool ExtractVideoOptions(const MediaConstraintsInterface* all_constraints,
263 cricket::VideoOptions* options) {
264 bool all_valid = true;
265
266 all_valid &= ExtractOption(all_constraints,
267 MediaConstraintsInterface::kNoiseReduction,
268 &(options->video_noise_reduction));
269
270 return all_valid;
271 }
272
273 } // anonymous namespace
274
275 namespace webrtc {
276
277 rtc::scoped_refptr<VideoSource> VideoSource::Create(
278 rtc::Thread* worker_thread,
279 cricket::VideoCapturer* capturer,
280 const webrtc::MediaConstraintsInterface* constraints,
281 bool remote) {
282 RTC_DCHECK(worker_thread != NULL);
283 RTC_DCHECK(capturer != NULL);
284 rtc::scoped_refptr<VideoSource> source(new rtc::RefCountedObject<VideoSource>(
285 worker_thread, capturer, remote));
286 source->Initialize(constraints);
287 return source;
288 }
289
290 rtc::scoped_refptr<VideoSource> VideoSource::Create(
291 rtc::Thread* worker_thread,
292 cricket::VideoCapturer* capturer,
293 bool remote) {
294 return Create(worker_thread, capturer, nullptr, remote);
295 }
296
297 VideoSource::VideoSource(rtc::Thread* worker_thread,
298 cricket::VideoCapturer* capturer,
299 bool remote)
300 : signaling_thread_(rtc::Thread::Current()),
301 worker_thread_(worker_thread),
302 video_capturer_(capturer),
303 started_(false),
304 state_(kInitializing),
305 remote_(remote) {
306 video_capturer_->SignalStateChange.connect(
307 this, &VideoSource::OnStateChange);
308 }
309
310 VideoSource::~VideoSource() {
311 video_capturer_->SignalStateChange.disconnect(this);
312 Stop();
313 }
314
315 void VideoSource::Initialize(
316 const webrtc::MediaConstraintsInterface* constraints) {
317 std::vector<cricket::VideoFormat> formats =
318 *video_capturer_->GetSupportedFormats();
319 if (formats.empty()) {
320 if (video_capturer_->IsScreencast()) {
321 // The screen capturer can accept any resolution and we will derive the
322 // format from the constraints if any.
323 // Note that this only affects tab capturing, not desktop capturing,
324 // since the desktop capturer does not respect the VideoFormat passed in.
325 formats.push_back(cricket::VideoFormat(kDefaultFormat));
326 } else {
327 // The VideoCapturer implementation doesn't support capability
328 // enumeration. We need to guess what the camera supports.
329 for (int i = 0; i < arraysize(kVideoFormats); ++i) {
330 formats.push_back(cricket::VideoFormat(kVideoFormats[i]));
331 }
332 }
333 }
334
335 if (constraints) {
336 MediaConstraintsInterface::Constraints mandatory_constraints =
337 constraints->GetMandatory();
338 MediaConstraintsInterface::Constraints optional_constraints;
339 optional_constraints = constraints->GetOptional();
340
341 if (video_capturer_->IsScreencast()) {
342 // Use the maxWidth and maxHeight allowed by constraints for screencast.
343 FromConstraintsForScreencast(mandatory_constraints, &(formats[0]));
344 }
345
346 formats = FilterFormats(mandatory_constraints, optional_constraints,
347 formats);
348 }
349
350 if (formats.size() == 0) {
351 LOG(LS_WARNING) << "Failed to find a suitable video format.";
352 SetState(kEnded);
353 return;
354 }
355
356 cricket::VideoOptions options;
357 if (!ExtractVideoOptions(constraints, &options)) {
358 LOG(LS_WARNING) << "Could not satisfy mandatory options.";
359 SetState(kEnded);
360 return;
361 }
362 options_.SetAll(options);
363 options_.is_screencast = rtc::Optional<bool>(video_capturer_->IsScreencast());
364
365 format_ = GetBestCaptureFormat(formats);
366 // Start the camera with our best guess.
367 // TODO(perkj): Should we try again with another format it it turns out that
368 // the camera doesn't produce frames with the correct format? Or will
369 // cricket::VideCapturer be able to re-scale / crop to the requested
370 // resolution?
371 if (!worker_thread_->Invoke<bool>(
372 rtc::Bind(&cricket::VideoCapturer::StartCapturing,
373 video_capturer_.get(), format_))) {
374 SetState(kEnded);
375 return;
376 }
377 started_ = true;
378 // Initialize hasn't succeeded until a successful state change has occurred.
379 }
380
381 void VideoSource::Stop() {
382 if (!started_) {
383 return;
384 }
385 started_ = false;
386 worker_thread_->Invoke<void>(
387 rtc::Bind(&cricket::VideoCapturer::Stop,
388 video_capturer_.get()));
389 }
390
391 void VideoSource::Restart() {
392 if (started_) {
393 return;
394 }
395 if (!worker_thread_->Invoke<bool>(
396 rtc::Bind(&cricket::VideoCapturer::StartCapturing,
397 video_capturer_.get(), format_))) {
398 SetState(kEnded);
399 return;
400 }
401 started_ = true;
402 }
403
404 void VideoSource::AddOrUpdateSink(
405 rtc::VideoSinkInterface<cricket::VideoFrame>* sink,
406 const rtc::VideoSinkWants& wants) {
407 worker_thread_->Invoke<void>(
408 rtc::Bind(&cricket::VideoCapturer::AddOrUpdateSink,
409 video_capturer_.get(), sink, wants));
410 }
411
412 void VideoSource::RemoveSink(
413 rtc::VideoSinkInterface<cricket::VideoFrame>* output) {
414 worker_thread_->Invoke<void>(
415 rtc::Bind(&cricket::VideoCapturer::RemoveSink,
416 video_capturer_.get(), output));
417 }
418
419 // OnStateChange listens to the cricket::VideoCapturer::SignalStateChange.
420 void VideoSource::OnStateChange(cricket::VideoCapturer* capturer,
421 cricket::CaptureState capture_state) {
422 if (rtc::Thread::Current() != signaling_thread_) {
423 invoker_.AsyncInvoke<void>(
424 signaling_thread_, rtc::Bind(&VideoSource::OnStateChange, this,
425 capturer, capture_state));
426 return;
427 }
428
429 if (capturer == video_capturer_.get()) {
430 SetState(GetReadyState(capture_state));
431 }
432 }
433
434 void VideoSource::SetState(SourceState new_state) {
435 if (state_ != new_state) {
436 state_ = new_state;
437 FireOnChanged();
438 }
439 }
440
441 } // namespace webrtc
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698