1 // Copyright 2014 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "third_party/blink/renderer/modules/mediastream/video_track_adapter.h"
14 #include "base/containers/flat_map.h"
15 #include "base/functional/bind.h"
16 #include "base/location.h"
17 #include "base/metrics/histogram_macros.h"
18 #include "base/sequence_checker.h"
19 #include "base/strings/string_number_conversions.h"
20 #include "base/task/bind_post_task.h"
21 #include "base/task/sequenced_task_runner.h"
22 #include "base/task/single_thread_task_runner.h"
23 #include "base/trace_event/trace_event.h"
24 #include "build/build_config.h"
25 #include "media/base/limits.h"
26 #include "media/base/timestamp_constants.h"
27 #include "media/base/video_util.h"
28 #include "third_party/abseil-cpp/absl/types/optional.h"
29 #include "third_party/blink/public/common/features.h"
30 #include "third_party/blink/public/platform/platform.h"
31 #include "third_party/blink/renderer/modules/mediastream/video_track_adapter_settings.h"
32 #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
33 #include "third_party/blink/renderer/platform/wtf/cross_thread_copier_base.h"
34 #include "third_party/blink/renderer/platform/wtf/cross_thread_copier_gfx.h"
35 #include "third_party/blink/renderer/platform/wtf/functional.h"
36 #include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h"
40 // Template specializations of [1], needed to be able to pass WTF callbacks
41 // that have VideoTrackAdapterSettings or gfx::Size parameters across threads.
43 // [1] third_party/blink/renderer/platform/wtf/cross_thread_copier.h.
45 struct CrossThreadCopier<blink::VideoTrackAdapterSettings>
46 : public CrossThreadCopierPassThrough<blink::VideoTrackAdapterSettings> {
47 STATIC_ONLY(CrossThreadCopier);
56 // Amount of frame intervals to wait before considering the source as muted, for
57 // the first frame and under normal conditions, respectively. First frame might
58 // take longer to arrive due to source startup.
59 const float kFirstFrameTimeoutInFrameIntervals = 100.0f;
60 const float kNormalFrameTimeoutInFrameIntervals = 25.0f;
62 // |kMaxDeltaDeviationFactor| is used to determine |max_delta_deviation_| which
63 // specifies the allowed deviation from |target_delta_| before dropping a frame.
64 // It's set to 20% to be aligned with the previous logic in this file.
65 constexpr float kMaxDeltaDeviationFactor = 0.2;
67 // If the delta between two frames is bigger than this, we will consider it to
68 // be invalid and reset the fps calculation.
69 constexpr base::TimeDelta kMaxTimeBetweenFrames = base::Milliseconds(1000);
71 constexpr base::TimeDelta kFrameRateChangeInterval = base::Seconds(1);
72 const double kFrameRateChangeRate = 0.01;
73 constexpr base::TimeDelta kFrameRateUpdateInterval = base::Seconds(5);
75 struct ComputedSettings {
77 double frame_rate = MediaStreamVideoSource::kDefaultFrameRate;
78 double last_updated_frame_rate = MediaStreamVideoSource::kDefaultFrameRate;
79 base::TimeDelta prev_frame_timestamp = base::TimeDelta::Max();
80 base::TimeTicks new_frame_rate_timestamp;
81 base::TimeTicks last_update_timestamp;
84 int ClampToValidDimension(int dimension) {
85 return std::min(static_cast<int>(media::limits::kMaxDimension),
86 std::max(0, dimension));
89 void ComputeFrameRate(const base::TimeDelta& frame_timestamp,
91 base::TimeDelta* prev_frame_timestamp) {
92 if (frame_timestamp == media::kNoTimestamp)
95 const double delta_ms =
96 (frame_timestamp - *prev_frame_timestamp).InMillisecondsF();
97 *prev_frame_timestamp = frame_timestamp;
101 *frame_rate = 200 / delta_ms + 0.8 * *frame_rate;
104 // Controls the frequency of settings updates based on frame rate changes.
105 // Returns |true| if over the last second the computed frame rate is
106 // consistently kFrameRateChangeRate different than the last reported value,
107 // or if there hasn't been any update in the last
108 // kFrameRateUpdateIntervalInSeconds seconds.
109 bool MaybeUpdateFrameRate(ComputedSettings* settings) {
110 base::TimeTicks now = base::TimeTicks::Now();
112 // Update frame rate if over the last second the computed frame rate has been
113 // consistently kFrameRateChangeIntervalInSeconds different than the last
115 if (std::abs(settings->frame_rate - settings->last_updated_frame_rate) >
116 settings->last_updated_frame_rate * kFrameRateChangeRate) {
117 if (now - settings->new_frame_rate_timestamp > kFrameRateChangeInterval) {
118 settings->new_frame_rate_timestamp = now;
119 settings->last_update_timestamp = now;
120 settings->last_updated_frame_rate = settings->frame_rate;
124 settings->new_frame_rate_timestamp = now;
127 // Update frame rate if it hasn't been updated in the last
128 // kFrameRateUpdateIntervalInSeconds seconds.
129 if (now - settings->last_update_timestamp > kFrameRateUpdateInterval) {
130 settings->last_update_timestamp = now;
131 settings->last_updated_frame_rate = settings->frame_rate;
137 VideoTrackAdapterSettings ReturnSettingsMaybeOverrideMaxFps(
138 const VideoTrackAdapterSettings& settings) {
139 VideoTrackAdapterSettings new_settings = settings;
140 absl::optional<double> max_fps_override =
141 Platform::Current()->GetWebRtcMaxCaptureFrameRate();
142 if (max_fps_override) {
143 DVLOG(1) << "Overriding max frame rate. Was="
144 << settings.max_frame_rate().value_or(-1)
145 << ", Now=" << *max_fps_override;
146 new_settings.set_max_frame_rate(*max_fps_override);
151 } // anonymous namespace
153 // VideoFrameResolutionAdapter is created on and lives on the video task runner.
154 // It does the resolution adaptation and delivers frames to all registered
155 // tracks on the video task runner. All method calls must be on the video task
157 class VideoTrackAdapter::VideoFrameResolutionAdapter
158 : public WTF::ThreadSafeRefCounted<VideoFrameResolutionAdapter> {
160 struct VideoTrackCallbacks {
161 VideoCaptureDeliverFrameInternalCallback frame_callback;
162 VideoCaptureNotifyFrameDroppedInternalCallback
163 notify_frame_dropped_callback;
164 DeliverEncodedVideoFrameInternalCallback encoded_frame_callback;
165 VideoCaptureSubCaptureTargetVersionInternalCallback
166 sub_capture_target_version_callback;
167 VideoTrackSettingsInternalCallback settings_callback;
168 VideoTrackFormatInternalCallback format_callback;
170 // Setting |max_frame_rate| to 0.0, means that no frame rate limitation
172 VideoFrameResolutionAdapter(
173 scoped_refptr<base::SingleThreadTaskRunner> reader_task_runner,
174 const VideoTrackAdapterSettings& settings,
175 base::WeakPtr<MediaStreamVideoSource> media_stream_video_source);
177 VideoFrameResolutionAdapter(const VideoFrameResolutionAdapter&) = delete;
178 VideoFrameResolutionAdapter& operator=(const VideoFrameResolutionAdapter&) =
181 // Add |frame_callback|, |encoded_frame_callback| to receive video frames on
182 // the video task runner, |sub_capture_target_version_callback| to receive
183 // notifications when a new sub-capture-target version is acknowledged, and
184 // |settings_callback| to set track settings on the main thread.
185 // |frame_callback| will however be released on the main render thread.
187 const MediaStreamVideoTrack* track,
188 VideoCaptureDeliverFrameInternalCallback frame_callback,
189 VideoCaptureNotifyFrameDroppedInternalCallback
190 notify_frame_dropped_callback,
191 DeliverEncodedVideoFrameInternalCallback encoded_frame_callback,
192 VideoCaptureSubCaptureTargetVersionInternalCallback
193 sub_capture_target_version_callback,
194 VideoTrackSettingsInternalCallback settings_callback,
195 VideoTrackFormatInternalCallback format_callback);
197 // Removes the callbacks associated with |track| if |track| has been added. It
198 // is ok to call RemoveCallbacks() even if |track| has not been added.
199 void RemoveCallbacks(const MediaStreamVideoTrack* track);
201 // Removes the callbacks associated with |track| if |track| has been added. It
202 // is ok to call RemoveAndGetCallbacks() even if the |track| has not been
203 // added. The function returns the callbacks if it was removed, or empty
204 // callbacks if |track| was not present in the adapter.
205 VideoTrackCallbacks RemoveAndGetCallbacks(const MediaStreamVideoTrack* track);
207 // The source has provided us with a frame.
209 scoped_refptr<media::VideoFrame> frame,
210 const base::TimeTicks& estimated_capture_time,
211 bool is_device_rotated);
212 // This method is called when a frame is dropped, whether dropped by the
213 // source (via VideoTrackAdapter::OnFrameDroppedOnVideoTaskRunner) or
214 // internally (in DeliverFrame).
215 void OnFrameDropped(media::VideoCaptureFrameDropReason reason);
217 void DeliverEncodedVideoFrame(scoped_refptr<EncodedVideoFrame> frame,
218 base::TimeTicks estimated_capture_time);
220 void NewSubCaptureTargetVersionOnVideoTaskRunner(
221 uint32_t sub_capture_target_version);
223 // Returns true if all arguments match with the output of this adapter.
224 bool SettingsMatch(const VideoTrackAdapterSettings& settings) const;
226 bool IsEmpty() const;
228 // Sets frame rate to 0.0 if frame monitor has detected muted state.
229 void ResetFrameRate();
232 virtual ~VideoFrameResolutionAdapter();
233 friend class WTF::ThreadSafeRefCounted<VideoFrameResolutionAdapter>;
236 scoped_refptr<media::VideoFrame> video_frame,
237 const base::TimeTicks& estimated_capture_time);
239 // Returns |true| if the input frame rate is higher that the requested max
240 // frame rate and |frame| should be dropped. If it returns true, |reason| is
241 // assigned to indicate the particular reason for the decision.
242 bool MaybeDropFrame(const media::VideoFrame& frame,
243 float source_frame_rate,
244 media::VideoCaptureFrameDropReason* reason);
246 // Updates track settings if either frame width, height or frame rate have
247 // changed since last update.
248 void MaybeUpdateTrackSettings(
249 const VideoTrackSettingsInternalCallback& settings_callback,
250 const media::VideoFrame& frame);
252 // Updates computed source format for all tracks if either frame width, height
253 // or frame rate have changed since last update.
254 void MaybeUpdateTracksFormat(const media::VideoFrame& frame);
256 // Bound to the video task runner.
257 SEQUENCE_CHECKER(video_sequence_checker_);
259 // The task runner where we will release VideoCaptureDeliverFrameCB
260 // registered in AddCallbacks.
261 const scoped_refptr<base::SingleThreadTaskRunner> renderer_task_runner_;
263 base::WeakPtr<MediaStreamVideoSource> media_stream_video_source_;
265 const VideoTrackAdapterSettings settings_;
267 // The target timestamp delta between video frames, corresponding to the max
269 const absl::optional<base::TimeDelta> target_delta_;
271 // The maximum allowed deviation from |target_delta_| before dropping a frame.
272 const absl::optional<base::TimeDelta> max_delta_deviation_;
274 // The timestamp of the last delivered video frame.
275 base::TimeDelta timestamp_last_delivered_frame_ = base::TimeDelta::Max();
277 // Stores the accumulated difference between |target_delta_| and the actual
278 // timestamp delta between frames that are delivered. Clamped to
279 // [-max_delta_deviation, target_delta_ / 2]. This is used to allow some
280 // frames to be closer than |target_delta_| in order to maintain
281 // |target_delta_| on average. Without it we may end up with an average fps
282 // that is half of max fps.
283 base::TimeDelta accumulated_drift_;
285 ComputedSettings track_settings_;
286 ComputedSettings source_format_settings_;
288 base::flat_map<const MediaStreamVideoTrack*, VideoTrackCallbacks> callbacks_;
291 VideoTrackAdapter::VideoFrameResolutionAdapter::VideoFrameResolutionAdapter(
292 scoped_refptr<base::SingleThreadTaskRunner> reader_task_runner,
293 const VideoTrackAdapterSettings& settings,
294 base::WeakPtr<MediaStreamVideoSource> media_stream_video_source)
295 : renderer_task_runner_(reader_task_runner),
296 media_stream_video_source_(media_stream_video_source),
297 settings_(ReturnSettingsMaybeOverrideMaxFps(settings)),
298 target_delta_(settings_.max_frame_rate()
299 ? absl::make_optional(base::Seconds(
300 1.0 / settings_.max_frame_rate().value()))
302 max_delta_deviation_(target_delta_
303 ? absl::make_optional(kMaxDeltaDeviationFactor *
304 target_delta_.value())
306 DVLOG(1) << __func__ << " max_framerate "
307 << settings.max_frame_rate().value_or(-1);
308 DCHECK(renderer_task_runner_.get());
309 DCHECK_CALLED_ON_VALID_SEQUENCE(video_sequence_checker_);
310 CHECK_NE(0, settings_.max_aspect_ratio());
313 VideoTrackAdapter::VideoFrameResolutionAdapter::~VideoFrameResolutionAdapter() {
314 DCHECK_CALLED_ON_VALID_SEQUENCE(video_sequence_checker_);
315 DCHECK(callbacks_.empty());
318 void VideoTrackAdapter::VideoFrameResolutionAdapter::AddCallbacks(
319 const MediaStreamVideoTrack* track,
320 VideoCaptureDeliverFrameInternalCallback frame_callback,
321 VideoCaptureNotifyFrameDroppedInternalCallback
322 notify_frame_dropped_callback,
323 DeliverEncodedVideoFrameInternalCallback encoded_frame_callback,
324 VideoCaptureSubCaptureTargetVersionInternalCallback
325 sub_capture_target_version_callback,
326 VideoTrackSettingsInternalCallback settings_callback,
327 VideoTrackFormatInternalCallback format_callback) {
328 DCHECK_CALLED_ON_VALID_SEQUENCE(video_sequence_checker_);
330 // The new track's settings should match the resolution adapter's current
331 // |track_settings_| as set for existing track(s) with matching
332 // VideoTrackAdapterSettings.
333 if (!callbacks_.empty() && track_settings_.frame_size.width() > 0 &&
334 track_settings_.frame_size.height() > 0) {
335 settings_callback.Run(track_settings_.frame_size,
336 track_settings_.frame_rate);
339 VideoTrackCallbacks track_callbacks = {
340 std::move(frame_callback),
341 std::move(notify_frame_dropped_callback),
342 std::move(encoded_frame_callback),
343 std::move(sub_capture_target_version_callback),
344 std::move(settings_callback),
345 std::move(format_callback)};
346 callbacks_.emplace(track, std::move(track_callbacks));
349 void VideoTrackAdapter::VideoFrameResolutionAdapter::RemoveCallbacks(
350 const MediaStreamVideoTrack* track) {
351 DCHECK_CALLED_ON_VALID_SEQUENCE(video_sequence_checker_);
352 callbacks_.erase(track);
355 VideoTrackAdapter::VideoFrameResolutionAdapter::VideoTrackCallbacks
356 VideoTrackAdapter::VideoFrameResolutionAdapter::RemoveAndGetCallbacks(
357 const MediaStreamVideoTrack* track) {
358 DCHECK_CALLED_ON_VALID_SEQUENCE(video_sequence_checker_);
359 VideoTrackCallbacks track_callbacks;
360 auto it = callbacks_.find(track);
361 if (it == callbacks_.end())
362 return track_callbacks;
364 track_callbacks = std::move(it->second);
365 callbacks_.erase(it);
366 return track_callbacks;
369 void VideoTrackAdapter::VideoFrameResolutionAdapter::DeliverFrame(
370 scoped_refptr<media::VideoFrame> video_frame,
371 const base::TimeTicks& estimated_capture_time,
372 bool is_device_rotated) {
373 DCHECK_CALLED_ON_VALID_SEQUENCE(video_sequence_checker_);
376 DLOG(ERROR) << "Incoming frame is not valid.";
378 media::VideoCaptureFrameDropReason::kResolutionAdapterFrameIsNotValid);
382 ComputeFrameRate(video_frame->timestamp(),
383 &source_format_settings_.frame_rate,
384 &source_format_settings_.prev_frame_timestamp);
385 MaybeUpdateTracksFormat(*video_frame);
387 double frame_rate = video_frame->metadata().frame_rate.value_or(
388 MediaStreamVideoSource::kUnknownFrameRate);
390 auto frame_drop_reason = media::VideoCaptureFrameDropReason::kNone;
391 if (MaybeDropFrame(*video_frame, frame_rate, &frame_drop_reason)) {
392 OnFrameDropped(frame_drop_reason);
396 // If the frame is a texture not backed up by GPU memory we don't apply
397 // cropping/scaling and deliver the frame as-is, leaving it up to the
398 // destination to rescale it. Otherwise, cropping and scaling is soft-applied
399 // before delivery for efficiency.
401 // TODO(crbug.com/362521): Allow cropping/scaling of non-GPU memory backed
403 if (video_frame->HasTextures() &&
404 video_frame->storage_type() !=
405 media::VideoFrame::STORAGE_GPU_MEMORY_BUFFER) {
406 DoDeliverFrame(std::move(video_frame), estimated_capture_time);
409 // The video frame we deliver may or may not get cropping and scaling
410 // soft-applied. Ultimately the listener will decide whether to use the
411 // |delivered_video_frame|.
412 scoped_refptr<media::VideoFrame> delivered_video_frame = video_frame;
414 gfx::Size desired_size;
415 CalculateDesiredSize(is_device_rotated, video_frame->natural_size(),
416 settings_, &desired_size);
417 if (desired_size != video_frame->natural_size()) {
418 // Get the largest centered rectangle with the same aspect ratio of
419 // |desired_size| that fits entirely inside of
420 // |video_frame->visible_rect()|. This will be the rect we need to crop the
421 // original frame to. From this rect, the original frame can be scaled down
422 // to |desired_size|.
423 gfx::Rect region_in_frame = media::ComputeLetterboxRegion(
424 video_frame->visible_rect(), desired_size);
426 // Some consumers (for example
427 // ImageCaptureFrameGrabber::SingleShotFrameHandler::ConvertAndDeliverFrame)
428 // don't support pixel format conversions when the source format is YUV with
429 // UV subsampled and vsible_rect().x() being odd. The conversion ends up
430 // miscomputing the UV plane and ends up with a VU plane leading to a blue
431 // face tint. Round x() to even to avoid. See crbug.com/1307304.
432 region_in_frame.set_x(region_in_frame.x() & ~1);
433 region_in_frame.set_y(region_in_frame.y() & ~1);
435 // ComputeLetterboxRegion() sometimes produces odd dimensions due to
436 // internal rounding errors; allow to round upwards if there's slack
437 // otherwise round downwards.
438 bool width_has_slack =
439 region_in_frame.right() < video_frame->visible_rect().right();
440 region_in_frame.set_width((region_in_frame.width() + width_has_slack) & ~1);
441 bool height_has_slack =
442 region_in_frame.bottom() < video_frame->visible_rect().bottom();
443 region_in_frame.set_height((region_in_frame.height() + height_has_slack) &
446 delivered_video_frame = media::VideoFrame::WrapVideoFrame(
447 video_frame, video_frame->format(), region_in_frame, desired_size);
448 if (!delivered_video_frame) {
449 OnFrameDropped(media::VideoCaptureFrameDropReason::
450 kResolutionAdapterWrappingFrameForCroppingFailed);
454 DVLOG(3) << "desired size " << desired_size.ToString()
455 << " output natural size "
456 << delivered_video_frame->natural_size().ToString()
457 << " output visible rect "
458 << delivered_video_frame->visible_rect().ToString();
460 DoDeliverFrame(std::move(delivered_video_frame), estimated_capture_time);
463 void VideoTrackAdapter::VideoFrameResolutionAdapter::DeliverEncodedVideoFrame(
464 scoped_refptr<EncodedVideoFrame> frame,
465 base::TimeTicks estimated_capture_time) {
466 DCHECK_CALLED_ON_VALID_SEQUENCE(video_sequence_checker_);
467 for (const auto& callback : callbacks_) {
468 callback.second.encoded_frame_callback.Run(frame, estimated_capture_time);
472 void VideoTrackAdapter::VideoFrameResolutionAdapter::
473 NewSubCaptureTargetVersionOnVideoTaskRunner(
474 uint32_t sub_capture_target_version) {
475 DCHECK_CALLED_ON_VALID_SEQUENCE(video_sequence_checker_);
476 for (const auto& callback : callbacks_) {
477 callback.second.sub_capture_target_version_callback.Run(
478 sub_capture_target_version);
482 bool VideoTrackAdapter::VideoFrameResolutionAdapter::SettingsMatch(
483 const VideoTrackAdapterSettings& settings) const {
484 DCHECK_CALLED_ON_VALID_SEQUENCE(video_sequence_checker_);
485 return settings_ == settings;
488 bool VideoTrackAdapter::VideoFrameResolutionAdapter::IsEmpty() const {
489 DCHECK_CALLED_ON_VALID_SEQUENCE(video_sequence_checker_);
490 return callbacks_.empty();
493 void VideoTrackAdapter::VideoFrameResolutionAdapter::DoDeliverFrame(
494 scoped_refptr<media::VideoFrame> video_frame,
495 const base::TimeTicks& estimated_capture_time) {
496 DCHECK_CALLED_ON_VALID_SEQUENCE(video_sequence_checker_);
497 if (callbacks_.empty()) {
499 media::VideoCaptureFrameDropReason::kResolutionAdapterHasNoCallbacks);
501 for (const auto& callback : callbacks_) {
502 MaybeUpdateTrackSettings(callback.second.settings_callback, *video_frame);
503 callback.second.frame_callback.Run(video_frame, estimated_capture_time);
507 void VideoTrackAdapter::VideoFrameResolutionAdapter::OnFrameDropped(
508 media::VideoCaptureFrameDropReason reason) {
509 DCHECK_CALLED_ON_VALID_SEQUENCE(video_sequence_checker_);
510 // Notify callbacks, such as
511 // MediaStreamVideoTrack::FrameDeliverer::NotifyFrameDroppedOnVideoTaskRunner.
512 for (const auto& callback : callbacks_) {
513 callback.second.notify_frame_dropped_callback.Run(reason);
517 bool VideoTrackAdapter::VideoFrameResolutionAdapter::MaybeDropFrame(
518 const media::VideoFrame& frame,
519 float source_frame_rate,
520 media::VideoCaptureFrameDropReason* reason) {
521 DCHECK_CALLED_ON_VALID_SEQUENCE(video_sequence_checker_);
523 // Never drop frames if the max frame rate has not been specified.
524 if (!settings_.max_frame_rate().has_value()) {
525 timestamp_last_delivered_frame_ = frame.timestamp();
529 const base::TimeDelta delta =
530 (frame.timestamp() - timestamp_last_delivered_frame_);
532 // Keep the frame if the time since the last frame is completely off.
533 if (delta.is_negative() || delta > kMaxTimeBetweenFrames) {
534 // Reset |timestamp_last_delivered_frame_| and |accumulated_drift|.
535 timestamp_last_delivered_frame_ = frame.timestamp();
536 accumulated_drift_ = base::Milliseconds(0.0);
540 DCHECK(target_delta_ && max_delta_deviation_);
541 if (delta < target_delta_.value() - max_delta_deviation_.value() -
542 accumulated_drift_) {
543 // Drop the frame because the input frame rate is too high.
544 *reason = media::VideoCaptureFrameDropReason::
545 kResolutionAdapterFrameRateIsHigherThanRequested;
549 // Keep the frame and store the accumulated drift.
550 timestamp_last_delivered_frame_ = frame.timestamp();
551 accumulated_drift_ += delta - target_delta_.value();
552 DCHECK_GE(accumulated_drift_, -max_delta_deviation_.value());
553 // Limit the maximum accumulated drift to half of the target delta. If we
554 // don't do this, it may happen that we output a series of frames too quickly
555 // after a period of no frames. There is no need to actively limit the minimum
556 // accumulated drift because that happens automatically when we drop frames
557 // that are too close in time.
558 accumulated_drift_ = std::min(accumulated_drift_, target_delta_.value() / 2);
562 void VideoTrackAdapter::VideoFrameResolutionAdapter::MaybeUpdateTrackSettings(
563 const VideoTrackSettingsInternalCallback& settings_callback,
564 const media::VideoFrame& frame) {
565 DCHECK_CALLED_ON_VALID_SEQUENCE(video_sequence_checker_);
566 ComputeFrameRate(frame.timestamp(), &track_settings_.frame_rate,
567 &track_settings_.prev_frame_timestamp);
568 if (MaybeUpdateFrameRate(&track_settings_) ||
569 frame.natural_size() != track_settings_.frame_size) {
570 track_settings_.frame_size = frame.natural_size();
571 settings_callback.Run(track_settings_.frame_size,
572 track_settings_.frame_rate);
575 void VideoTrackAdapter::VideoFrameResolutionAdapter::MaybeUpdateTracksFormat(
576 const media::VideoFrame& frame) {
577 DCHECK_CALLED_ON_VALID_SEQUENCE(video_sequence_checker_);
578 if (MaybeUpdateFrameRate(&source_format_settings_) ||
579 frame.natural_size() != track_settings_.frame_size) {
580 source_format_settings_.frame_size = frame.natural_size();
581 media::VideoCaptureFormat source_format;
582 source_format.frame_size = source_format_settings_.frame_size;
583 source_format.frame_rate = source_format_settings_.frame_rate;
584 for (const auto& callback : callbacks_)
585 callback.second.format_callback.Run(source_format);
589 void VideoTrackAdapter::VideoFrameResolutionAdapter::ResetFrameRate() {
590 DCHECK_CALLED_ON_VALID_SEQUENCE(video_sequence_checker_);
591 for (const auto& callback : callbacks_) {
592 callback.second.settings_callback.Run(track_settings_.frame_size, 0.0);
596 VideoTrackAdapter::VideoTrackAdapter(
597 scoped_refptr<base::SequencedTaskRunner> video_task_runner,
598 base::WeakPtr<MediaStreamVideoSource> media_stream_video_source)
599 : video_task_runner_(video_task_runner),
600 media_stream_video_source_(media_stream_video_source),
601 renderer_task_runner_(base::SingleThreadTaskRunner::GetCurrentDefault()),
604 old_frame_counter_snapshot_(0),
605 source_frame_rate_(0.0f) {
606 DCHECK(video_task_runner);
609 VideoTrackAdapter::~VideoTrackAdapter() {
610 DCHECK(adapters_.empty());
611 DCHECK(!monitoring_frame_rate_timer_);
614 void VideoTrackAdapter::AddTrack(
615 const MediaStreamVideoTrack* track,
616 VideoCaptureDeliverFrameCB frame_callback,
617 VideoCaptureNotifyFrameDroppedCB notify_frame_dropped_callback,
618 EncodedVideoFrameCB encoded_frame_callback,
619 VideoCaptureSubCaptureTargetVersionCB sub_capture_target_version_callback,
620 VideoTrackSettingsCallback settings_callback,
621 VideoTrackFormatCallback format_callback,
622 const VideoTrackAdapterSettings& settings) {
623 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
626 *video_task_runner_, FROM_HERE,
628 &VideoTrackAdapter::AddTrackOnVideoTaskRunner,
629 WTF::CrossThreadUnretained(this), WTF::CrossThreadUnretained(track),
630 CrossThreadBindRepeating(std::move(frame_callback)),
631 CrossThreadBindRepeating(std::move(notify_frame_dropped_callback)),
632 CrossThreadBindRepeating(std::move(encoded_frame_callback)),
633 CrossThreadBindRepeating(
634 std::move(sub_capture_target_version_callback)),
635 CrossThreadBindRepeating(std::move(settings_callback)),
636 CrossThreadBindRepeating(std::move(format_callback)), settings));
639 void VideoTrackAdapter::AddTrackOnVideoTaskRunner(
640 const MediaStreamVideoTrack* track,
641 VideoCaptureDeliverFrameInternalCallback frame_callback,
642 VideoCaptureNotifyFrameDroppedInternalCallback
643 notify_frame_dropped_callback,
644 DeliverEncodedVideoFrameInternalCallback encoded_frame_callback,
645 VideoCaptureSubCaptureTargetVersionInternalCallback
646 sub_capture_target_version_callback,
647 VideoTrackSettingsInternalCallback settings_callback,
648 VideoTrackFormatInternalCallback format_callback,
649 const VideoTrackAdapterSettings& settings) {
650 DCHECK(video_task_runner_->RunsTasksInCurrentSequence());
651 scoped_refptr<VideoFrameResolutionAdapter> adapter;
652 for (const auto& frame_adapter : adapters_) {
653 if (frame_adapter->SettingsMatch(settings)) {
654 adapter = frame_adapter.get();
658 if (!adapter.get()) {
659 adapter = base::MakeRefCounted<VideoFrameResolutionAdapter>(
660 renderer_task_runner_, settings, media_stream_video_source_);
661 adapters_.push_back(adapter);
664 adapter->AddCallbacks(track, std::move(frame_callback),
665 std::move(notify_frame_dropped_callback),
666 std::move(encoded_frame_callback),
667 std::move(sub_capture_target_version_callback),
668 std::move(settings_callback),
669 std::move(format_callback));
672 void VideoTrackAdapter::RemoveTrack(const MediaStreamVideoTrack* track) {
673 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
675 *video_task_runner_, FROM_HERE,
676 CrossThreadBindOnce(&VideoTrackAdapter::RemoveTrackOnVideoTaskRunner,
677 WrapRefCounted(this), CrossThreadUnretained(track)));
680 void VideoTrackAdapter::ReconfigureTrack(
681 const MediaStreamVideoTrack* track,
682 const VideoTrackAdapterSettings& settings) {
683 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
686 *video_task_runner_, FROM_HERE,
687 CrossThreadBindOnce(&VideoTrackAdapter::ReconfigureTrackOnVideoTaskRunner,
688 WrapRefCounted(this), CrossThreadUnretained(track),
692 void VideoTrackAdapter::StartFrameMonitoring(
693 double source_frame_rate,
694 const OnMutedCallback& on_muted_callback) {
695 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
697 VideoTrackAdapter::OnMutedCallback bound_on_muted_callback =
698 base::BindPostTaskToCurrentDefault(on_muted_callback);
701 *video_task_runner_, FROM_HERE,
703 &VideoTrackAdapter::StartFrameMonitoringOnVideoTaskRunner,
704 WrapRefCounted(this),
705 CrossThreadBindRepeating(std::move(bound_on_muted_callback)),
709 void VideoTrackAdapter::StopFrameMonitoring() {
710 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
712 *video_task_runner_, FROM_HERE,
714 &VideoTrackAdapter::StopFrameMonitoringOnVideoTaskRunner,
715 WrapRefCounted(this)));
718 void VideoTrackAdapter::SetSourceFrameSize(const gfx::Size& source_frame_size) {
719 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
721 *video_task_runner_, FROM_HERE,
723 &VideoTrackAdapter::SetSourceFrameSizeOnVideoTaskRunner,
724 WrapRefCounted(this), source_frame_size));
727 bool VideoTrackAdapter::CalculateDesiredSize(
729 const gfx::Size& original_input_size,
730 const VideoTrackAdapterSettings& settings,
731 gfx::Size* desired_size) {
732 // Perform all the rescaling computations as if the device was never rotated.
734 is_rotated ? original_input_size.height() : original_input_size.width();
736 is_rotated ? original_input_size.width() : original_input_size.height();
738 DCHECK_GE(height, 0);
740 // Rescale only if a target size was provided in |settings|.
741 if (settings.target_size()) {
742 // Adjust the size of the frame to the maximum allowed size.
744 ClampToValidDimension(std::min(width, settings.target_size()->width()));
745 height = ClampToValidDimension(
746 std::min(height, settings.target_size()->height()));
748 // If the area of the frame is zero, ignore aspect-ratio correction.
749 if (width * height > 0) {
750 double ratio = static_cast<double>(width) / height;
751 DCHECK(std::isfinite(ratio));
752 if (ratio > settings.max_aspect_ratio() ||
753 ratio < settings.min_aspect_ratio()) {
754 // Make sure |min_aspect_ratio| <= |desired_ratio| <=
755 // |max_aspect_ratio|.
756 double desired_ratio =
757 std::max(std::min(ratio, settings.max_aspect_ratio()),
758 settings.min_aspect_ratio());
759 DCHECK(std::isfinite(desired_ratio));
760 DCHECK_NE(desired_ratio, 0.0);
762 if (ratio < desired_ratio) {
763 double desired_height_fp = (height * ratio) / desired_ratio;
764 DCHECK(std::isfinite(desired_height_fp));
765 height = static_cast<int>(desired_height_fp);
766 // Make sure we scale to an even height to avoid rounding errors
767 height = (height + 1) & ~1;
768 } else if (ratio > desired_ratio) {
769 double desired_width_fp = (width * desired_ratio) / ratio;
770 DCHECK(std::isfinite(desired_width_fp));
771 width = static_cast<int>(desired_width_fp);
772 // Make sure we scale to an even width to avoid rounding errors.
773 width = (width + 1) & ~1;
777 } else if (width > media::limits::kMaxDimension ||
778 height > media::limits::kMaxDimension) {
782 // Output back taking device rotation into account.
784 is_rotated ? gfx::Size(height, width) : gfx::Size(width, height);
788 void VideoTrackAdapter::StartFrameMonitoringOnVideoTaskRunner(
789 OnMutedInternalCallback on_muted_callback,
790 double source_frame_rate) {
791 DCHECK(video_task_runner_->RunsTasksInCurrentSequence());
792 DCHECK(!monitoring_frame_rate_timer_);
794 on_muted_callback_ = std::move(on_muted_callback);
795 monitoring_frame_rate_timer_ = std::make_unique<LowPrecisionTimer>(
797 ConvertToBaseRepeatingCallback(CrossThreadBindRepeating(
798 &VideoTrackAdapter::CheckFramesReceivedOnVideoTaskRunner,
799 WrapRefCounted(this))));
801 // If the source does not know the frame rate, set one by default.
802 if (source_frame_rate == 0.0f)
803 source_frame_rate = MediaStreamVideoSource::kDefaultFrameRate;
804 source_frame_rate_ = source_frame_rate;
805 DVLOG(1) << "Monitoring frame creation, first (large) delay: "
806 << (kFirstFrameTimeoutInFrameIntervals / source_frame_rate_) << "s";
807 old_frame_counter_snapshot_ = frame_counter_;
808 monitoring_frame_rate_timer_->StartOneShot(
809 base::Seconds(kFirstFrameTimeoutInFrameIntervals / source_frame_rate_));
812 void VideoTrackAdapter::StopFrameMonitoringOnVideoTaskRunner() {
813 DCHECK(video_task_runner_->RunsTasksInCurrentSequence());
814 if (!monitoring_frame_rate_timer_) {
818 monitoring_frame_rate_timer_->Shutdown();
819 monitoring_frame_rate_timer_.reset();
820 on_muted_callback_ = OnMutedInternalCallback();
823 void VideoTrackAdapter::SetSourceFrameSizeOnVideoTaskRunner(
824 const gfx::Size& source_frame_size) {
825 DCHECK(video_task_runner_->RunsTasksInCurrentSequence());
826 source_frame_size_ = source_frame_size;
829 void VideoTrackAdapter::RemoveTrackOnVideoTaskRunner(
830 const MediaStreamVideoTrack* track) {
831 DCHECK(video_task_runner_->RunsTasksInCurrentSequence());
832 for (auto* it = adapters_.begin(); it != adapters_.end(); ++it) {
833 (*it)->RemoveCallbacks(track);
834 if ((*it)->IsEmpty()) {
841 void VideoTrackAdapter::ReconfigureTrackOnVideoTaskRunner(
842 const MediaStreamVideoTrack* track,
843 const VideoTrackAdapterSettings& settings) {
844 DCHECK(video_task_runner_->RunsTasksInCurrentSequence());
846 VideoFrameResolutionAdapter::VideoTrackCallbacks track_callbacks;
848 for (auto* it = adapters_.begin(); it != adapters_.end(); ++it) {
849 track_callbacks = (*it)->RemoveAndGetCallbacks(track);
850 if (!track_callbacks.frame_callback)
852 if ((*it)->IsEmpty()) {
853 DCHECK(track_callbacks.frame_callback);
859 // If the track was found, re-add it with new settings.
860 if (track_callbacks.frame_callback) {
861 AddTrackOnVideoTaskRunner(
862 track, std::move(track_callbacks.frame_callback),
863 std::move(track_callbacks.notify_frame_dropped_callback),
864 std::move(track_callbacks.encoded_frame_callback),
865 std::move(track_callbacks.sub_capture_target_version_callback),
866 std::move(track_callbacks.settings_callback),
867 std::move(track_callbacks.format_callback), settings);
871 void VideoTrackAdapter::DeliverFrameOnVideoTaskRunner(
872 scoped_refptr<media::VideoFrame> video_frame,
873 base::TimeTicks estimated_capture_time) {
874 DCHECK(video_task_runner_->RunsTasksInCurrentSequence());
875 TRACE_EVENT0("media", "VideoTrackAdapter::DeliverFrameOnVideoTaskRunner");
878 bool is_device_rotated = false;
879 // TODO(guidou): Use actual device information instead of this heuristic to
880 // detect frames from rotated devices. https://crbug.com/722748
881 if (source_frame_size_ &&
882 video_frame->natural_size().width() == source_frame_size_->height() &&
883 video_frame->natural_size().height() == source_frame_size_->width()) {
884 is_device_rotated = true;
886 for (const auto& adapter : adapters_) {
887 adapter->DeliverFrame(video_frame, estimated_capture_time,
892 void VideoTrackAdapter::DeliverEncodedVideoFrameOnVideoTaskRunner(
893 scoped_refptr<EncodedVideoFrame> frame,
894 base::TimeTicks estimated_capture_time) {
895 DCHECK(video_task_runner_->RunsTasksInCurrentSequence());
896 TRACE_EVENT0("media",
897 "VideoTrackAdapter::DeliverEncodedVideoFrameOnVideoTaskRunner");
898 for (const auto& adapter : adapters_)
899 adapter->DeliverEncodedVideoFrame(frame, estimated_capture_time);
902 void VideoTrackAdapter::OnFrameDroppedOnVideoTaskRunner(
903 media::VideoCaptureFrameDropReason reason) {
904 DCHECK(video_task_runner_->RunsTasksInCurrentSequence());
905 TRACE_EVENT0("media", "VideoTrackAdapter::OnFrameDroppedOnVideoTaskRunner");
906 for (const auto& adapter : adapters_) {
907 adapter->OnFrameDropped(reason);
911 void VideoTrackAdapter::NewSubCaptureTargetVersionOnVideoTaskRunner(
912 uint32_t sub_capture_target_version) {
913 DCHECK(video_task_runner_->RunsTasksInCurrentSequence());
916 "VideoTrackAdapter::NewSubCaptureTargetVersionOnVideoTaskRunner");
917 for (const auto& adapter : adapters_) {
918 adapter->NewSubCaptureTargetVersionOnVideoTaskRunner(
919 sub_capture_target_version);
923 void VideoTrackAdapter::CheckFramesReceivedOnVideoTaskRunner() {
924 DCHECK(video_task_runner_->RunsTasksInCurrentSequence());
926 DVLOG_IF(1, old_frame_counter_snapshot_ == frame_counter_)
927 << "No frames have passed, setting source as Muted.";
928 bool muted_state = old_frame_counter_snapshot_ == frame_counter_;
929 if (muted_state_ != muted_state) {
930 on_muted_callback_.Run(muted_state);
931 muted_state_ = muted_state;
933 for (const auto& adapter : adapters_)
934 adapter->ResetFrameRate();
938 old_frame_counter_snapshot_ = frame_counter_;
939 monitoring_frame_rate_timer_->StartOneShot(
940 base::Seconds(kNormalFrameTimeoutInFrameIntervals / source_frame_rate_));