ambient_animation_player.cc 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. // Copyright 2022 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ash/ambient/ui/ambient_animation_player.h"
  5. #include <string>
  6. #include <utility>
  7. #include "ash/ambient/ui/ambient_animation_progress_tracker.h"
  8. #include "ash/public/cpp/ambient/ambient_ui_model.h"
  9. #include "ash/utility/lottie_util.h"
  10. #include "base/check.h"
  11. #include "base/logging.h"
  12. #include "base/no_destructor.h"
  13. #include "base/strings/strcat.h"
  14. #include "base/strings/string_piece.h"
  15. #include "base/time/time.h"
  16. #include "cc/paint/skottie_marker.h"
  17. #include "cc/paint/skottie_wrapper.h"
  18. #include "third_party/abseil-cpp/absl/types/optional.h"
  19. #include "ui/views/controls/animated_image_view.h"
  20. namespace ash {
  21. namespace {
  22. absl::optional<base::TimeDelta> FindCycleRestartTimestamp(
  23. const cc::SkottieWrapper& skottie) {
  24. static const base::NoDestructor<std::string> kRestartMarkerName(
  25. base::StrCat({kLottieCustomizableIdPrefix, "_Marker_CycleRestart"}));
  26. DCHECK(skottie.is_valid());
  27. absl::optional<base::TimeDelta> restart_timestamp;
  28. for (const cc::SkottieMarker& marker : skottie.GetAllMarkers()) {
  29. if (marker.name != *kRestartMarkerName) {
  30. continue;
  31. } else if (restart_timestamp.has_value()) {
  32. LOG(DFATAL) << "Multiple markers with name " << *kRestartMarkerName
  33. << " found in animation file. Defaulting to first one.";
  34. } else {
  35. // |marker.begin_time| is a normalized timestamp in range [0, 1), where 1
  36. // is the animation's cycle duration.
  37. restart_timestamp.emplace(base::Seconds(skottie.duration()) *
  38. marker.begin_time);
  39. DVLOG(1) << "Found restart marker at timestamp " << *restart_timestamp;
  40. }
  41. }
  42. return restart_timestamp;
  43. }
  44. } // namespace
  45. AmbientAnimationPlayer::AmbientAnimationPlayer(
  46. views::AnimatedImageView* animated_image_view,
  47. AmbientAnimationProgressTracker* progress_tracker)
  48. : animated_image_view_(animated_image_view),
  49. progress_tracker_(progress_tracker) {
  50. DCHECK(animated_image_view_);
  51. lottie::Animation* animation = animated_image_view_->animated_image();
  52. DCHECK(animation);
  53. DCHECK(progress_tracker_);
  54. lottie::Animation::PlaybackConfig playback_config;
  55. // If there are existing animations in this ambient mode session, start
  56. // playing from the existing animations' timestamps. This gives rough
  57. // synchronization across animations. The animations' timestamps should not
  58. // diverge over time because AnimatedImageView makes time steps that
  59. // ultimately reflect real time (i.e. it does not step by a fixed amount each
  60. // frame). Thus, there may be transient periods where the animations diverge
  61. // due to random system instability, but they should all converge again
  62. // eventually.
  63. if (progress_tracker_->HasActiveAnimations()) {
  64. AmbientAnimationProgressTracker::ImmutableParams immutable_params =
  65. progress_tracker_->GetImmutableParams();
  66. AmbientAnimationProgressTracker::Progress global_progress =
  67. progress_tracker_->GetGlobalProgress();
  68. playback_config = {
  69. immutable_params.scheduled_cycles,
  70. global_progress.current_timestamp * immutable_params.total_duration,
  71. global_progress.num_completed_cycles, immutable_params.style};
  72. } else {
  73. absl::optional<base::TimeDelta> cycle_restart_timestamp_found =
  74. FindCycleRestartTimestamp(*animation->skottie());
  75. if (cycle_restart_timestamp_found.has_value()) {
  76. cycle_restart_timestamp_ = *cycle_restart_timestamp_found;
  77. if (cycle_restart_timestamp_ >= animation->GetAnimationDuration()) {
  78. LOG(DFATAL) << "Animation has invalid cycle restart timestamp "
  79. << cycle_restart_timestamp_ << ". Total cycle duration "
  80. << animation->GetAnimationDuration();
  81. }
  82. } else {
  83. DVLOG(1) << "Restart marker not found in animation. Defaulting to cycle "
  84. "restart at timestamp 0";
  85. DCHECK(cycle_restart_timestamp_.is_zero());
  86. }
  87. playback_config = lottie::Animation::PlaybackConfig(
  88. {{base::TimeDelta(), animation->GetAnimationDuration()},
  89. {cycle_restart_timestamp_, animation->GetAnimationDuration()}},
  90. /*initial_offset=*/base::TimeDelta(), /*initial_completed_cycles=*/0,
  91. lottie::Animation::Style::kLoop);
  92. }
  93. animation->SetPlaybackSpeed(
  94. AmbientUiModel::Get()->animation_playback_speed());
  95. progress_tracker_->RegisterAnimation(animation);
  96. animated_image_view_->Play(std::move(playback_config));
  97. }
  98. AmbientAnimationPlayer::~AmbientAnimationPlayer() {
  99. animated_image_view_->Stop();
  100. }
  101. } // namespace ash