blob: 50467c368d32d5396c26e612cfc2642dac6a5f38 [file] [log] [blame]
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001// libjingle
2// Copyright 2010 Google Inc.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are met:
6//
7// 1. Redistributions of source code must retain the above copyright notice,
8// this list of conditions and the following disclaimer.
9// 2. Redistributions in binary form must reproduce the above copyright notice,
10// this list of conditions and the following disclaimer in the documentation
11// and/or other materials provided with the distribution.
12// 3. The name of the author may not be used to endorse or promote products
13// derived from this software without specific prior written permission.
14//
15// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
16// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
17// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
18// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25//
26// Implementation file of class VideoCapturer.
27
28#include "talk/media/base/videocapturer.h"
29
30#include <algorithm>
31
32#if !defined(DISABLE_YUV)
33#include "libyuv/scale_argb.h"
34#endif
35#include "talk/base/common.h"
36#include "talk/base/logging.h"
37#include "talk/base/systeminfo.h"
38#include "talk/media/base/videoprocessor.h"
39
40#if defined(HAVE_WEBRTC_VIDEO)
41#include "talk/media/webrtc/webrtcvideoframe.h"
42#endif // HAVE_WEBRTC_VIDEO
43
44
45namespace cricket {
46
47namespace {
48
49// TODO(thorcarpenter): This is a BIG hack to flush the system with black
50// frames. Frontends should coordinate to update the video state of a muted
51// user. When all frontends to this consider removing the black frame business.
52const int kNumBlackFramesOnMute = 30;
53
54// MessageHandler constants.
55enum {
56 MSG_DO_PAUSE = 0,
57 MSG_DO_UNPAUSE,
58 MSG_STATE_CHANGE
59};
60
61static const int64 kMaxDistance = ~(static_cast<int64>(1) << 63);
62static const int kYU12Penalty = 16; // Needs to be higher than MJPG index.
63static const int kDefaultScreencastFps = 5;
64typedef talk_base::TypedMessageData<CaptureState> StateChangeParams;
65
66} // namespace
67
68/////////////////////////////////////////////////////////////////////
69// Implementation of struct CapturedFrame
70/////////////////////////////////////////////////////////////////////
71CapturedFrame::CapturedFrame()
72 : width(0),
73 height(0),
74 fourcc(0),
75 pixel_width(0),
76 pixel_height(0),
77 elapsed_time(0),
78 time_stamp(0),
79 data_size(0),
80 rotation(0),
81 data(NULL) {}
82
83// TODO(fbarchard): Remove this function once lmimediaengine stops using it.
84bool CapturedFrame::GetDataSize(uint32* size) const {
85 if (!size || data_size == CapturedFrame::kUnknownDataSize) {
86 return false;
87 }
88 *size = data_size;
89 return true;
90}
91
92/////////////////////////////////////////////////////////////////////
93// Implementation of class VideoCapturer
94/////////////////////////////////////////////////////////////////////
95VideoCapturer::VideoCapturer() : thread_(talk_base::Thread::Current()) {
96 Construct();
97}
98
99VideoCapturer::VideoCapturer(talk_base::Thread* thread) : thread_(thread) {
100 Construct();
101}
102
103void VideoCapturer::Construct() {
104 ClearAspectRatio();
105 enable_camera_list_ = false;
106 capture_state_ = CS_STOPPED;
107 SignalFrameCaptured.connect(this, &VideoCapturer::OnFrameCaptured);
108 scaled_width_ = 0;
109 scaled_height_ = 0;
wu@webrtc.orgcadf9042013-08-30 21:24:16 +0000110 screencast_max_pixels_ = 0;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000111 muted_ = false;
112 black_frame_count_down_ = kNumBlackFramesOnMute;
113}
114
115const std::vector<VideoFormat>* VideoCapturer::GetSupportedFormats() const {
116 return &filtered_supported_formats_;
117}
118
119bool VideoCapturer::StartCapturing(const VideoFormat& capture_format) {
120 CaptureState result = Start(capture_format);
121 const bool success = (result == CS_RUNNING) || (result == CS_STARTING);
122 if (!success) {
123 return false;
124 }
125 if (result == CS_RUNNING) {
126 SetCaptureState(result);
127 }
128 return true;
129}
130
131void VideoCapturer::UpdateAspectRatio(int ratio_w, int ratio_h) {
132 if (ratio_w == 0 || ratio_h == 0) {
133 LOG(LS_WARNING) << "UpdateAspectRatio ignored invalid ratio: "
134 << ratio_w << "x" << ratio_h;
135 return;
136 }
137 ratio_w_ = ratio_w;
138 ratio_h_ = ratio_h;
139}
140
141void VideoCapturer::ClearAspectRatio() {
142 ratio_w_ = 0;
143 ratio_h_ = 0;
144}
145
146// Override this to have more control of how your device is started/stopped.
147bool VideoCapturer::Pause(bool pause) {
148 if (pause) {
149 if (capture_state() == CS_PAUSED) {
150 return true;
151 }
152 bool is_running = capture_state() == CS_STARTING ||
153 capture_state() == CS_RUNNING;
154 if (!is_running) {
155 LOG(LS_ERROR) << "Cannot pause a stopped camera.";
156 return false;
157 }
158 LOG(LS_INFO) << "Pausing a camera.";
159 talk_base::scoped_ptr<VideoFormat> capture_format_when_paused(
160 capture_format_ ? new VideoFormat(*capture_format_) : NULL);
161 Stop();
162 SetCaptureState(CS_PAUSED);
163 // If you override this function be sure to restore the capture format
164 // after calling Stop().
165 SetCaptureFormat(capture_format_when_paused.get());
166 } else { // Unpause.
167 if (capture_state() != CS_PAUSED) {
168 LOG(LS_WARNING) << "Cannot unpause a camera that hasn't been paused.";
169 return false;
170 }
171 if (!capture_format_) {
172 LOG(LS_ERROR) << "Missing capture_format_, cannot unpause a camera.";
173 return false;
174 }
175 if (muted_) {
176 LOG(LS_WARNING) << "Camera cannot be unpaused while muted.";
177 return false;
178 }
179 LOG(LS_INFO) << "Unpausing a camera.";
180 if (!Start(*capture_format_)) {
181 LOG(LS_ERROR) << "Camera failed to start when unpausing.";
182 return false;
183 }
184 }
185 return true;
186}
187
188bool VideoCapturer::Restart(const VideoFormat& capture_format) {
189 if (!IsRunning()) {
190 return StartCapturing(capture_format);
191 }
192
193 if (GetCaptureFormat() != NULL && *GetCaptureFormat() == capture_format) {
194 // The reqested format is the same; nothing to do.
195 return true;
196 }
197
198 Stop();
199 return StartCapturing(capture_format);
200}
201
202bool VideoCapturer::MuteToBlackThenPause(bool muted) {
203 if (muted == IsMuted()) {
204 return true;
205 }
206
207 LOG(LS_INFO) << (muted ? "Muting" : "Unmuting") << " this video capturer.";
208 muted_ = muted; // Do this before calling Pause().
209 if (muted) {
210 // Reset black frame count down.
211 black_frame_count_down_ = kNumBlackFramesOnMute;
212 // Following frames will be overritten with black, then the camera will be
213 // paused.
214 return true;
215 }
216 // Start the camera.
217 thread_->Clear(this, MSG_DO_PAUSE);
218 return Pause(false);
219}
220
221void VideoCapturer::SetSupportedFormats(
222 const std::vector<VideoFormat>& formats) {
223 supported_formats_ = formats;
224 UpdateFilteredSupportedFormats();
225}
226
227bool VideoCapturer::GetBestCaptureFormat(const VideoFormat& format,
228 VideoFormat* best_format) {
229 // TODO(fbarchard): Directly support max_format.
230 UpdateFilteredSupportedFormats();
231 const std::vector<VideoFormat>* supported_formats = GetSupportedFormats();
232
233 if (supported_formats->empty()) {
234 return false;
235 }
236 LOG(LS_INFO) << " Capture Requested " << format.ToString();
237 int64 best_distance = kMaxDistance;
238 std::vector<VideoFormat>::const_iterator best = supported_formats->end();
239 std::vector<VideoFormat>::const_iterator i;
240 for (i = supported_formats->begin(); i != supported_formats->end(); ++i) {
241 int64 distance = GetFormatDistance(format, *i);
242 // TODO(fbarchard): Reduce to LS_VERBOSE if/when camera capture is
243 // relatively bug free.
244 LOG(LS_INFO) << " Supported " << i->ToString() << " distance " << distance;
245 if (distance < best_distance) {
246 best_distance = distance;
247 best = i;
248 }
249 }
250 if (supported_formats->end() == best) {
251 LOG(LS_ERROR) << " No acceptable camera format found";
252 return false;
253 }
254
255 if (best_format) {
256 best_format->width = best->width;
257 best_format->height = best->height;
258 best_format->fourcc = best->fourcc;
259 best_format->interval = talk_base::_max(format.interval, best->interval);
260 LOG(LS_INFO) << " Best " << best_format->ToString() << " Interval "
261 << best_format->interval << " distance " << best_distance;
262 }
263 return true;
264}
265
266void VideoCapturer::AddVideoProcessor(VideoProcessor* video_processor) {
267 talk_base::CritScope cs(&crit_);
268 ASSERT(std::find(video_processors_.begin(), video_processors_.end(),
269 video_processor) == video_processors_.end());
270 video_processors_.push_back(video_processor);
271}
272
273bool VideoCapturer::RemoveVideoProcessor(VideoProcessor* video_processor) {
274 talk_base::CritScope cs(&crit_);
275 VideoProcessors::iterator found = std::find(
276 video_processors_.begin(), video_processors_.end(), video_processor);
277 if (found == video_processors_.end()) {
278 return false;
279 }
280 video_processors_.erase(found);
281 return true;
282}
283
284void VideoCapturer::ConstrainSupportedFormats(const VideoFormat& max_format) {
285 max_format_.reset(new VideoFormat(max_format));
286 LOG(LS_VERBOSE) << " ConstrainSupportedFormats " << max_format.ToString();
287 UpdateFilteredSupportedFormats();
288}
289
290std::string VideoCapturer::ToString(const CapturedFrame* captured_frame) const {
291 std::string fourcc_name = GetFourccName(captured_frame->fourcc) + " ";
292 for (std::string::const_iterator i = fourcc_name.begin();
293 i < fourcc_name.end(); ++i) {
294 // Test character is printable; Avoid isprint() which asserts on negatives.
295 if (*i < 32 || *i >= 127) {
296 fourcc_name = "";
297 break;
298 }
299 }
300
301 std::ostringstream ss;
302 ss << fourcc_name << captured_frame->width << "x" << captured_frame->height
303 << "x" << VideoFormat::IntervalToFps(captured_frame->elapsed_time);
304 return ss.str();
305}
306
307void VideoCapturer::OnFrameCaptured(VideoCapturer*,
308 const CapturedFrame* captured_frame) {
309 if (muted_) {
310 if (black_frame_count_down_ == 0) {
311 thread_->Post(this, MSG_DO_PAUSE, NULL);
312 } else {
313 --black_frame_count_down_;
314 }
315 }
316
317 if (SignalVideoFrame.is_empty()) {
318 return;
319 }
320#if defined(HAVE_WEBRTC_VIDEO)
321#define VIDEO_FRAME_NAME WebRtcVideoFrame
322#endif
323#if defined(VIDEO_FRAME_NAME)
324#if !defined(DISABLE_YUV)
325 if (IsScreencast()) {
326 int scaled_width, scaled_height;
wu@webrtc.orgcadf9042013-08-30 21:24:16 +0000327 if (screencast_max_pixels_ > 0) {
328 ComputeScaleMaxPixels(captured_frame->width, captured_frame->height,
329 screencast_max_pixels_, &scaled_width, &scaled_height);
330 } else {
331 int desired_screencast_fps = capture_format_.get() ?
332 VideoFormat::IntervalToFps(capture_format_->interval) :
333 kDefaultScreencastFps;
334 ComputeScale(captured_frame->width, captured_frame->height,
335 desired_screencast_fps, &scaled_width, &scaled_height);
336 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000337
338 if (scaled_width != scaled_width_ || scaled_height != scaled_height_) {
339 LOG(LS_VERBOSE) << "Scaling Screencast from "
340 << captured_frame->width << "x"
341 << captured_frame->height << " to "
342 << scaled_width << "x" << scaled_height;
343 scaled_width_ = scaled_width;
344 scaled_height_ = scaled_height;
345 }
346 if (FOURCC_ARGB == captured_frame->fourcc &&
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000347 (scaled_width != captured_frame->width ||
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000348 scaled_height != captured_frame->height)) {
349 CapturedFrame* scaled_frame = const_cast<CapturedFrame*>(captured_frame);
350 // Compute new width such that width * height is less than maximum but
351 // maintains original captured frame aspect ratio.
352 // Round down width to multiple of 4 so odd width won't round up beyond
353 // maximum, and so chroma channel is even width to simplify spatial
354 // resampling.
355 libyuv::ARGBScale(reinterpret_cast<const uint8*>(captured_frame->data),
356 captured_frame->width * 4, captured_frame->width,
357 captured_frame->height,
358 reinterpret_cast<uint8*>(scaled_frame->data),
359 scaled_width * 4, scaled_width, scaled_height,
360 libyuv::kFilterBilinear);
361 scaled_frame->width = scaled_width;
362 scaled_frame->height = scaled_height;
363 scaled_frame->data_size = scaled_width * 4 * scaled_height;
364 }
365 }
366#endif // !DISABLE_YUV
367 // Size to crop captured frame to. This adjusts the captured frames
368 // aspect ratio to match the final view aspect ratio, considering pixel
369 // aspect ratio and rotation. The final size may be scaled down by video
370 // adapter to better match ratio_w_ x ratio_h_.
371 // Note that abs() of frame height is passed in, because source may be
372 // inverted, but output will be positive.
373 int desired_width = captured_frame->width;
374 int desired_height = captured_frame->height;
375
376 // TODO(fbarchard): Improve logic to pad or crop.
377 // MJPG can crop vertically, but not horizontally. This logic disables crop.
378 // Alternatively we could pad the image with black, or implement a 2 step
379 // crop.
380 bool can_crop = true;
381 if (captured_frame->fourcc == FOURCC_MJPG) {
382 float cam_aspect = static_cast<float>(captured_frame->width) /
383 static_cast<float>(captured_frame->height);
384 float view_aspect = static_cast<float>(ratio_w_) /
385 static_cast<float>(ratio_h_);
386 can_crop = cam_aspect <= view_aspect;
387 }
388 if (can_crop && !IsScreencast()) {
389 // TODO(ronghuawu): The capturer should always produce the native
390 // resolution and the cropping should be done in downstream code.
391 ComputeCrop(ratio_w_, ratio_h_, captured_frame->width,
392 abs(captured_frame->height), captured_frame->pixel_width,
393 captured_frame->pixel_height, captured_frame->rotation,
394 &desired_width, &desired_height);
395 }
396
397 VIDEO_FRAME_NAME i420_frame;
398 if (!i420_frame.Init(captured_frame, desired_width, desired_height)) {
399 // TODO(fbarchard): LOG more information about captured frame attributes.
400 LOG(LS_ERROR) << "Couldn't convert to I420! "
401 << "From " << ToString(captured_frame) << " To "
402 << desired_width << " x " << desired_height;
403 return;
404 }
405 if (!muted_ && !ApplyProcessors(&i420_frame)) {
406 // Processor dropped the frame.
407 return;
408 }
409 if (muted_) {
410 i420_frame.SetToBlack();
411 }
412 SignalVideoFrame(this, &i420_frame);
413#endif // VIDEO_FRAME_NAME
414}
415
416void VideoCapturer::SetCaptureState(CaptureState state) {
417 if (state == capture_state_) {
418 // Don't trigger a state changed callback if the state hasn't changed.
419 return;
420 }
421 StateChangeParams* state_params = new StateChangeParams(state);
422 capture_state_ = state;
423 thread_->Post(this, MSG_STATE_CHANGE, state_params);
424}
425
426void VideoCapturer::OnMessage(talk_base::Message* message) {
427 switch (message->message_id) {
428 case MSG_STATE_CHANGE: {
429 talk_base::scoped_ptr<StateChangeParams> p(
430 static_cast<StateChangeParams*>(message->pdata));
431 SignalStateChange(this, p->data());
432 break;
433 }
434 case MSG_DO_PAUSE: {
435 Pause(true);
436 break;
437 }
438 case MSG_DO_UNPAUSE: {
439 Pause(false);
440 break;
441 }
442 default: {
443 ASSERT(false);
444 }
445 }
446}
447
448// Get the distance between the supported and desired formats.
449// Prioritization is done according to this algorithm:
450// 1) Width closeness. If not same, we prefer wider.
451// 2) Height closeness. If not same, we prefer higher.
452// 3) Framerate closeness. If not same, we prefer faster.
453// 4) Compression. If desired format has a specific fourcc, we need exact match;
454// otherwise, we use preference.
455int64 VideoCapturer::GetFormatDistance(const VideoFormat& desired,
456 const VideoFormat& supported) {
457 int64 distance = kMaxDistance;
458
459 // Check fourcc.
460 uint32 supported_fourcc = CanonicalFourCC(supported.fourcc);
461 int64 delta_fourcc = kMaxDistance;
462 if (FOURCC_ANY == desired.fourcc) {
463 // Any fourcc is OK for the desired. Use preference to find best fourcc.
464 std::vector<uint32> preferred_fourccs;
465 if (!GetPreferredFourccs(&preferred_fourccs)) {
466 return distance;
467 }
468
469 for (size_t i = 0; i < preferred_fourccs.size(); ++i) {
470 if (supported_fourcc == CanonicalFourCC(preferred_fourccs[i])) {
471 delta_fourcc = i;
472#ifdef LINUX
473 // For HD avoid YU12 which is a software conversion and has 2 bugs
474 // b/7326348 b/6960899. Reenable when fixed.
475 if (supported.height >= 720 && (supported_fourcc == FOURCC_YU12 ||
476 supported_fourcc == FOURCC_YV12)) {
477 delta_fourcc += kYU12Penalty;
478 }
479#endif
480 break;
481 }
482 }
483 } else if (supported_fourcc == CanonicalFourCC(desired.fourcc)) {
484 delta_fourcc = 0; // Need exact match.
485 }
486
487 if (kMaxDistance == delta_fourcc) {
488 // Failed to match fourcc.
489 return distance;
490 }
491
492 // Check resolution and fps.
493 int desired_width = desired.width;
494 int desired_height = desired.height;
495 int64 delta_w = supported.width - desired_width;
496 int64 supported_fps = VideoFormat::IntervalToFps(supported.interval);
497 int64 delta_fps =
498 supported_fps - VideoFormat::IntervalToFps(desired.interval);
499 // Check height of supported height compared to height we would like it to be.
500 int64 aspect_h =
501 desired_width ? supported.width * desired_height / desired_width
502 : desired_height;
503 int64 delta_h = supported.height - aspect_h;
504
505 distance = 0;
506 // Set high penalty if the supported format is lower than the desired format.
507 // 3x means we would prefer down to down to 3/4, than up to double.
508 // But we'd prefer up to double than down to 1/2. This is conservative,
509 // strongly avoiding going down in resolution, similar to
510 // the old method, but not completely ruling it out in extreme situations.
511 // It also ignores framerate, which is often very low at high resolutions.
512 // TODO(fbarchard): Improve logic to use weighted factors.
513 static const int kDownPenalty = -3;
514 if (delta_w < 0) {
515 delta_w = delta_w * kDownPenalty;
516 }
517 if (delta_h < 0) {
518 delta_h = delta_h * kDownPenalty;
519 }
520 // Require camera fps to be at least 80% of what is requested if resolution
521 // matches.
522 // Require camera fps to be at least 96% of what is requested, or higher,
523 // if resolution differs. 96% allows for slight variations in fps. e.g. 29.97
524 if (delta_fps < 0) {
525 int64 min_desirable_fps = delta_w ?
526 VideoFormat::IntervalToFps(desired.interval) * 29 / 30 :
527 VideoFormat::IntervalToFps(desired.interval) * 24 / 30;
528 delta_fps = -delta_fps;
529 if (supported_fps < min_desirable_fps) {
530 distance |= static_cast<int64>(1) << 62;
531 } else {
532 distance |= static_cast<int64>(1) << 15;
533 }
534 }
535
536 // 12 bits for width and height and 8 bits for fps and fourcc.
537 distance |=
538 (delta_w << 28) | (delta_h << 16) | (delta_fps << 8) | delta_fourcc;
539
540 return distance;
541}
542
543bool VideoCapturer::ApplyProcessors(VideoFrame* video_frame) {
544 bool drop_frame = false;
545 talk_base::CritScope cs(&crit_);
546 for (VideoProcessors::iterator iter = video_processors_.begin();
547 iter != video_processors_.end(); ++iter) {
548 (*iter)->OnFrame(kDummyVideoSsrc, video_frame, &drop_frame);
549 if (drop_frame) {
550 return false;
551 }
552 }
553 return true;
554}
555
556void VideoCapturer::UpdateFilteredSupportedFormats() {
557 filtered_supported_formats_.clear();
558 filtered_supported_formats_ = supported_formats_;
559 if (!max_format_) {
560 return;
561 }
562 std::vector<VideoFormat>::iterator iter = filtered_supported_formats_.begin();
563 while (iter != filtered_supported_formats_.end()) {
564 if (ShouldFilterFormat(*iter)) {
565 iter = filtered_supported_formats_.erase(iter);
566 } else {
567 ++iter;
568 }
569 }
570 if (filtered_supported_formats_.empty()) {
571 // The device only captures at resolutions higher than |max_format_| this
572 // indicates that |max_format_| should be ignored as it is better to capture
573 // at too high a resolution than to not capture at all.
574 filtered_supported_formats_ = supported_formats_;
575 }
576}
577
578bool VideoCapturer::ShouldFilterFormat(const VideoFormat& format) const {
579 if (!enable_camera_list_) {
580 return false;
581 }
582 return format.width > max_format_->width ||
583 format.height > max_format_->height;
584}
585
586} // namespace cricket