OLD | NEW |
| (Empty) |
1 /* | |
2 * libjingle | |
3 * Copyright 2012 Google Inc. | |
4 * | |
5 * Redistribution and use in source and binary forms, with or without | |
6 * modification, are permitted provided that the following conditions are met: | |
7 * | |
8 * 1. Redistributions of source code must retain the above copyright notice, | |
9 * this list of conditions and the following disclaimer. | |
10 * 2. Redistributions in binary form must reproduce the above copyright notice, | |
11 * this list of conditions and the following disclaimer in the documentation | |
12 * and/or other materials provided with the distribution. | |
13 * 3. The name of the author may not be used to endorse or promote products | |
14 * derived from this software without specific prior written permission. | |
15 * | |
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED | |
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | |
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | |
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | |
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | |
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | |
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
26 */ | |
27 | |
28 #include "talk/app/webrtc/videosource.h" | |
29 | |
30 #include <cstdlib> | |
31 #include <vector> | |
32 | |
33 #include "talk/app/webrtc/mediaconstraintsinterface.h" | |
34 #include "talk/session/media/channelmanager.h" | |
35 #include "webrtc/base/arraysize.h" | |
36 | |
37 using cricket::CaptureState; | |
38 using webrtc::MediaConstraintsInterface; | |
39 using webrtc::MediaSourceInterface; | |
40 | |
41 namespace { | |
42 | |
43 const double kRoundingTruncation = 0.0005; | |
44 | |
45 enum { | |
46 MSG_VIDEOCAPTURESTATECONNECT, | |
47 MSG_VIDEOCAPTURESTATEDISCONNECT, | |
48 MSG_VIDEOCAPTURESTATECHANGE, | |
49 }; | |
50 | |
51 // Default resolution. If no constraint is specified, this is the resolution we | |
52 // will use. | |
53 static const cricket::VideoFormatPod kDefaultFormat = | |
54 {640, 480, FPS_TO_INTERVAL(30), cricket::FOURCC_ANY}; | |
55 | |
56 // List of formats used if the camera doesn't support capability enumeration. | |
57 static const cricket::VideoFormatPod kVideoFormats[] = { | |
58 {1920, 1080, FPS_TO_INTERVAL(30), cricket::FOURCC_ANY}, | |
59 {1280, 720, FPS_TO_INTERVAL(30), cricket::FOURCC_ANY}, | |
60 {960, 720, FPS_TO_INTERVAL(30), cricket::FOURCC_ANY}, | |
61 {640, 360, FPS_TO_INTERVAL(30), cricket::FOURCC_ANY}, | |
62 {640, 480, FPS_TO_INTERVAL(30), cricket::FOURCC_ANY}, | |
63 {320, 240, FPS_TO_INTERVAL(30), cricket::FOURCC_ANY}, | |
64 {320, 180, FPS_TO_INTERVAL(30), cricket::FOURCC_ANY} | |
65 }; | |
66 | |
67 MediaSourceInterface::SourceState | |
68 GetReadyState(cricket::CaptureState state) { | |
69 switch (state) { | |
70 case cricket::CS_STARTING: | |
71 return MediaSourceInterface::kInitializing; | |
72 case cricket::CS_RUNNING: | |
73 return MediaSourceInterface::kLive; | |
74 case cricket::CS_FAILED: | |
75 case cricket::CS_NO_DEVICE: | |
76 case cricket::CS_STOPPED: | |
77 return MediaSourceInterface::kEnded; | |
78 case cricket::CS_PAUSED: | |
79 return MediaSourceInterface::kMuted; | |
80 default: | |
81 ASSERT(false && "GetReadyState unknown state"); | |
82 } | |
83 return MediaSourceInterface::kEnded; | |
84 } | |
85 | |
86 void SetUpperLimit(int new_limit, int* original_limit) { | |
87 if (*original_limit < 0 || new_limit < *original_limit) | |
88 *original_limit = new_limit; | |
89 } | |
90 | |
91 // Updates |format_upper_limit| from |constraint|. | |
92 // If constraint.maxFoo is smaller than format_upper_limit.foo, | |
93 // set format_upper_limit.foo to constraint.maxFoo. | |
94 void SetUpperLimitFromConstraint( | |
95 const MediaConstraintsInterface::Constraint& constraint, | |
96 cricket::VideoFormat* format_upper_limit) { | |
97 if (constraint.key == MediaConstraintsInterface::kMaxWidth) { | |
98 int value = rtc::FromString<int>(constraint.value); | |
99 SetUpperLimit(value, &(format_upper_limit->width)); | |
100 } else if (constraint.key == MediaConstraintsInterface::kMaxHeight) { | |
101 int value = rtc::FromString<int>(constraint.value); | |
102 SetUpperLimit(value, &(format_upper_limit->height)); | |
103 } | |
104 } | |
105 | |
106 // Fills |format_out| with the max width and height allowed by |constraints|. | |
107 void FromConstraintsForScreencast( | |
108 const MediaConstraintsInterface::Constraints& constraints, | |
109 cricket::VideoFormat* format_out) { | |
110 typedef MediaConstraintsInterface::Constraints::const_iterator | |
111 ConstraintsIterator; | |
112 | |
113 cricket::VideoFormat upper_limit(-1, -1, 0, 0); | |
114 for (ConstraintsIterator constraints_it = constraints.begin(); | |
115 constraints_it != constraints.end(); ++constraints_it) | |
116 SetUpperLimitFromConstraint(*constraints_it, &upper_limit); | |
117 | |
118 if (upper_limit.width >= 0) | |
119 format_out->width = upper_limit.width; | |
120 if (upper_limit.height >= 0) | |
121 format_out->height = upper_limit.height; | |
122 } | |
123 | |
124 // Returns true if |constraint| is fulfilled. |format_out| can differ from | |
125 // |format_in| if the format is changed by the constraint. Ie - the frame rate | |
126 // can be changed by setting maxFrameRate. | |
127 bool NewFormatWithConstraints( | |
128 const MediaConstraintsInterface::Constraint& constraint, | |
129 const cricket::VideoFormat& format_in, | |
130 bool mandatory, | |
131 cricket::VideoFormat* format_out) { | |
132 ASSERT(format_out != NULL); | |
133 *format_out = format_in; | |
134 | |
135 if (constraint.key == MediaConstraintsInterface::kMinWidth) { | |
136 int value = rtc::FromString<int>(constraint.value); | |
137 return (value <= format_in.width); | |
138 } else if (constraint.key == MediaConstraintsInterface::kMaxWidth) { | |
139 int value = rtc::FromString<int>(constraint.value); | |
140 return (value >= format_in.width); | |
141 } else if (constraint.key == MediaConstraintsInterface::kMinHeight) { | |
142 int value = rtc::FromString<int>(constraint.value); | |
143 return (value <= format_in.height); | |
144 } else if (constraint.key == MediaConstraintsInterface::kMaxHeight) { | |
145 int value = rtc::FromString<int>(constraint.value); | |
146 return (value >= format_in.height); | |
147 } else if (constraint.key == MediaConstraintsInterface::kMinFrameRate) { | |
148 int value = rtc::FromString<int>(constraint.value); | |
149 return (value <= cricket::VideoFormat::IntervalToFps(format_in.interval)); | |
150 } else if (constraint.key == MediaConstraintsInterface::kMaxFrameRate) { | |
151 int value = rtc::FromString<int>(constraint.value); | |
152 if (value == 0) { | |
153 if (mandatory) { | |
154 // TODO(ronghuawu): Convert the constraint value to float when sub-1fps | |
155 // is supported by the capturer. | |
156 return false; | |
157 } else { | |
158 value = 1; | |
159 } | |
160 } | |
161 if (value <= cricket::VideoFormat::IntervalToFps(format_in.interval)) | |
162 format_out->interval = cricket::VideoFormat::FpsToInterval(value); | |
163 return true; | |
164 } else if (constraint.key == MediaConstraintsInterface::kMinAspectRatio) { | |
165 double value = rtc::FromString<double>(constraint.value); | |
166 // The aspect ratio in |constraint.value| has been converted to a string and | |
167 // back to a double, so it may have a rounding error. | |
168 // E.g if the value 1/3 is converted to a string, the string will not have | |
169 // infinite length. | |
170 // We add a margin of 0.0005 which is high enough to detect the same aspect | |
171 // ratio but small enough to avoid matching wrong aspect ratios. | |
172 double ratio = static_cast<double>(format_in.width) / format_in.height; | |
173 return (value <= ratio + kRoundingTruncation); | |
174 } else if (constraint.key == MediaConstraintsInterface::kMaxAspectRatio) { | |
175 double value = rtc::FromString<double>(constraint.value); | |
176 double ratio = static_cast<double>(format_in.width) / format_in.height; | |
177 // Subtract 0.0005 to avoid rounding problems. Same as above. | |
178 const double kRoundingTruncation = 0.0005; | |
179 return (value >= ratio - kRoundingTruncation); | |
180 } else if (constraint.key == MediaConstraintsInterface::kNoiseReduction) { | |
181 // These are actually options, not constraints, so they can be satisfied | |
182 // regardless of the format. | |
183 return true; | |
184 } | |
185 LOG(LS_WARNING) << "Found unknown MediaStream constraint. Name:" | |
186 << constraint.key << " Value:" << constraint.value; | |
187 return false; | |
188 } | |
189 | |
190 // Removes cricket::VideoFormats from |formats| that don't meet |constraint|. | |
191 void FilterFormatsByConstraint( | |
192 const MediaConstraintsInterface::Constraint& constraint, | |
193 bool mandatory, | |
194 std::vector<cricket::VideoFormat>* formats) { | |
195 std::vector<cricket::VideoFormat>::iterator format_it = | |
196 formats->begin(); | |
197 while (format_it != formats->end()) { | |
198 // Modify the format_it to fulfill the constraint if possible. | |
199 // Delete it otherwise. | |
200 if (!NewFormatWithConstraints(constraint, (*format_it), | |
201 mandatory, &(*format_it))) { | |
202 format_it = formats->erase(format_it); | |
203 } else { | |
204 ++format_it; | |
205 } | |
206 } | |
207 } | |
208 | |
209 // Returns a vector of cricket::VideoFormat that best match |constraints|. | |
210 std::vector<cricket::VideoFormat> FilterFormats( | |
211 const MediaConstraintsInterface::Constraints& mandatory, | |
212 const MediaConstraintsInterface::Constraints& optional, | |
213 const std::vector<cricket::VideoFormat>& supported_formats) { | |
214 typedef MediaConstraintsInterface::Constraints::const_iterator | |
215 ConstraintsIterator; | |
216 std::vector<cricket::VideoFormat> candidates = supported_formats; | |
217 | |
218 for (ConstraintsIterator constraints_it = mandatory.begin(); | |
219 constraints_it != mandatory.end(); ++constraints_it) | |
220 FilterFormatsByConstraint(*constraints_it, true, &candidates); | |
221 | |
222 if (candidates.size() == 0) | |
223 return candidates; | |
224 | |
225 // Ok - all mandatory checked and we still have a candidate. | |
226 // Let's try filtering using the optional constraints. | |
227 for (ConstraintsIterator constraints_it = optional.begin(); | |
228 constraints_it != optional.end(); ++constraints_it) { | |
229 std::vector<cricket::VideoFormat> current_candidates = candidates; | |
230 FilterFormatsByConstraint(*constraints_it, false, ¤t_candidates); | |
231 if (current_candidates.size() > 0) { | |
232 candidates = current_candidates; | |
233 } | |
234 } | |
235 | |
236 // We have done as good as we can to filter the supported resolutions. | |
237 return candidates; | |
238 } | |
239 | |
240 // Find the format that best matches the default video size. | |
241 // Constraints are optional and since the performance of a video call | |
242 // might be bad due to bitrate limitations, CPU, and camera performance, | |
243 // it is better to select a resolution that is as close as possible to our | |
244 // default and still meets the contraints. | |
245 const cricket::VideoFormat& GetBestCaptureFormat( | |
246 const std::vector<cricket::VideoFormat>& formats) { | |
247 ASSERT(formats.size() > 0); | |
248 | |
249 int default_area = kDefaultFormat.width * kDefaultFormat.height; | |
250 | |
251 std::vector<cricket::VideoFormat>::const_iterator it = formats.begin(); | |
252 std::vector<cricket::VideoFormat>::const_iterator best_it = formats.begin(); | |
253 int best_diff_area = std::abs(default_area - it->width * it->height); | |
254 int64_t best_diff_interval = kDefaultFormat.interval; | |
255 for (; it != formats.end(); ++it) { | |
256 int diff_area = std::abs(default_area - it->width * it->height); | |
257 int64_t diff_interval = std::abs(kDefaultFormat.interval - it->interval); | |
258 if (diff_area < best_diff_area || | |
259 (diff_area == best_diff_area && diff_interval < best_diff_interval)) { | |
260 best_diff_area = diff_area; | |
261 best_diff_interval = diff_interval; | |
262 best_it = it; | |
263 } | |
264 } | |
265 return *best_it; | |
266 } | |
267 | |
268 // Set |option| to the highest-priority value of |key| in the constraints. | |
269 // Return false if the key is mandatory, and the value is invalid. | |
270 bool ExtractOption(const MediaConstraintsInterface* all_constraints, | |
271 const std::string& key, | |
272 rtc::Optional<bool>* option) { | |
273 size_t mandatory = 0; | |
274 bool value; | |
275 if (FindConstraint(all_constraints, key, &value, &mandatory)) { | |
276 *option = rtc::Optional<bool>(value); | |
277 return true; | |
278 } | |
279 | |
280 return mandatory == 0; | |
281 } | |
282 | |
283 // Search |all_constraints| for known video options. Apply all options that are | |
284 // found with valid values, and return false if any mandatory video option was | |
285 // found with an invalid value. | |
286 bool ExtractVideoOptions(const MediaConstraintsInterface* all_constraints, | |
287 cricket::VideoOptions* options) { | |
288 bool all_valid = true; | |
289 | |
290 all_valid &= ExtractOption(all_constraints, | |
291 MediaConstraintsInterface::kNoiseReduction, | |
292 &(options->video_noise_reduction)); | |
293 | |
294 return all_valid; | |
295 } | |
296 | |
297 } // anonymous namespace | |
298 | |
299 namespace webrtc { | |
300 | |
301 rtc::scoped_refptr<VideoSource> VideoSource::Create( | |
302 cricket::ChannelManager* channel_manager, | |
303 cricket::VideoCapturer* capturer, | |
304 const webrtc::MediaConstraintsInterface* constraints, | |
305 bool remote) { | |
306 ASSERT(channel_manager != NULL); | |
307 ASSERT(capturer != NULL); | |
308 rtc::scoped_refptr<VideoSource> source(new rtc::RefCountedObject<VideoSource>( | |
309 channel_manager, capturer, remote)); | |
310 source->Initialize(constraints); | |
311 return source; | |
312 } | |
313 | |
314 VideoSource::VideoSource(cricket::ChannelManager* channel_manager, | |
315 cricket::VideoCapturer* capturer, | |
316 bool remote) | |
317 : channel_manager_(channel_manager), | |
318 video_capturer_(capturer), | |
319 state_(kInitializing), | |
320 remote_(remote) { | |
321 channel_manager_->SignalVideoCaptureStateChange.connect( | |
322 this, &VideoSource::OnStateChange); | |
323 } | |
324 | |
325 VideoSource::~VideoSource() { | |
326 channel_manager_->StopVideoCapture(video_capturer_.get(), format_); | |
327 channel_manager_->SignalVideoCaptureStateChange.disconnect(this); | |
328 } | |
329 | |
330 void VideoSource::Initialize( | |
331 const webrtc::MediaConstraintsInterface* constraints) { | |
332 | |
333 std::vector<cricket::VideoFormat> formats = | |
334 channel_manager_->GetSupportedFormats(video_capturer_.get()); | |
335 if (formats.empty()) { | |
336 if (video_capturer_->IsScreencast()) { | |
337 // The screen capturer can accept any resolution and we will derive the | |
338 // format from the constraints if any. | |
339 // Note that this only affects tab capturing, not desktop capturing, | |
340 // since the desktop capturer does not respect the VideoFormat passed in. | |
341 formats.push_back(cricket::VideoFormat(kDefaultFormat)); | |
342 } else { | |
343 // The VideoCapturer implementation doesn't support capability | |
344 // enumeration. We need to guess what the camera supports. | |
345 for (int i = 0; i < arraysize(kVideoFormats); ++i) { | |
346 formats.push_back(cricket::VideoFormat(kVideoFormats[i])); | |
347 } | |
348 } | |
349 } | |
350 | |
351 if (constraints) { | |
352 MediaConstraintsInterface::Constraints mandatory_constraints = | |
353 constraints->GetMandatory(); | |
354 MediaConstraintsInterface::Constraints optional_constraints; | |
355 optional_constraints = constraints->GetOptional(); | |
356 | |
357 if (video_capturer_->IsScreencast()) { | |
358 // Use the maxWidth and maxHeight allowed by constraints for screencast. | |
359 FromConstraintsForScreencast(mandatory_constraints, &(formats[0])); | |
360 } | |
361 | |
362 formats = FilterFormats(mandatory_constraints, optional_constraints, | |
363 formats); | |
364 } | |
365 | |
366 if (formats.size() == 0) { | |
367 LOG(LS_WARNING) << "Failed to find a suitable video format."; | |
368 SetState(kEnded); | |
369 return; | |
370 } | |
371 | |
372 cricket::VideoOptions options; | |
373 if (!ExtractVideoOptions(constraints, &options)) { | |
374 LOG(LS_WARNING) << "Could not satisfy mandatory options."; | |
375 SetState(kEnded); | |
376 return; | |
377 } | |
378 options_.SetAll(options); | |
379 | |
380 format_ = GetBestCaptureFormat(formats); | |
381 // Start the camera with our best guess. | |
382 // TODO(perkj): Should we try again with another format it it turns out that | |
383 // the camera doesn't produce frames with the correct format? Or will | |
384 // cricket::VideCapturer be able to re-scale / crop to the requested | |
385 // resolution? | |
386 if (!channel_manager_->StartVideoCapture(video_capturer_.get(), format_)) { | |
387 SetState(kEnded); | |
388 return; | |
389 } | |
390 // Initialize hasn't succeeded until a successful state change has occurred. | |
391 } | |
392 | |
393 void VideoSource::Stop() { | |
394 channel_manager_->StopVideoCapture(video_capturer_.get(), format_); | |
395 } | |
396 | |
397 void VideoSource::Restart() { | |
398 if (!channel_manager_->StartVideoCapture(video_capturer_.get(), format_)) { | |
399 SetState(kEnded); | |
400 return; | |
401 } | |
402 for (auto* sink : sinks_) { | |
403 channel_manager_->AddVideoSink(video_capturer_.get(), sink); | |
404 } | |
405 } | |
406 | |
407 void VideoSource::AddSink( | |
408 rtc::VideoSinkInterface<cricket::VideoFrame>* output) { | |
409 sinks_.push_back(output); | |
410 channel_manager_->AddVideoSink(video_capturer_.get(), output); | |
411 } | |
412 | |
413 void VideoSource::RemoveSink( | |
414 rtc::VideoSinkInterface<cricket::VideoFrame>* output) { | |
415 sinks_.remove(output); | |
416 channel_manager_->RemoveVideoSink(video_capturer_.get(), output); | |
417 } | |
418 | |
419 // OnStateChange listens to the ChannelManager::SignalVideoCaptureStateChange. | |
420 // This signal is triggered for all video capturers. Not only the one we are | |
421 // interested in. | |
422 void VideoSource::OnStateChange(cricket::VideoCapturer* capturer, | |
423 cricket::CaptureState capture_state) { | |
424 if (capturer == video_capturer_.get()) { | |
425 SetState(GetReadyState(capture_state)); | |
426 } | |
427 } | |
428 | |
429 void VideoSource::SetState(SourceState new_state) { | |
430 // TODO(hbos): Temporarily disabled VERIFY due to webrtc:4776. | |
431 // if (VERIFY(state_ != new_state)) { | |
432 if (state_ != new_state) { | |
433 state_ = new_state; | |
434 FireOnChanged(); | |
435 } | |
436 } | |
437 | |
438 } // namespace webrtc | |
OLD | NEW |