1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171

media / gpu / android / surface_chooser_helper.cc [blame]

// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/gpu/android/surface_chooser_helper.h"

#include <memory>

#include "base/time/default_tick_clock.h"
#include "base/time/tick_clock.h"
#include "media/gpu/android/android_video_surface_chooser.h"
#include "media/gpu/android/promotion_hint_aggregator_impl.h"

namespace media {

namespace {

// Number of frames to defer overlays for when entering fullscreen.  This lets
// blink relayout settle down a bit.  If overlay positions were synchronous,
// then we wouldn't need this.
enum { kFrameDelayForFullscreenLayout = 15 };

// How often do we let the surface chooser try for an overlay?  While we'll
// retry if some relevant state changes on our side (e.g., fullscreen state),
// there's plenty of state that we don't know about (e.g., power efficiency,
// memory pressure => cancelling an old overlay, etc.).  We just let the chooser
// retry every once in a while for those things.
constexpr base::TimeDelta RetryChooserTimeout = base::Seconds(5);

}  // namespace

SurfaceChooserHelper::SurfaceChooserHelper(
    std::unique_ptr<AndroidVideoSurfaceChooser> surface_chooser,
    bool is_overlay_required,
    bool promote_secure_only,
    bool always_use_texture_owner,
    std::unique_ptr<PromotionHintAggregator> promotion_hint_aggregator,
    const base::TickClock* tick_clock)
    : surface_chooser_(std::move(surface_chooser)),
      is_overlay_required_(is_overlay_required),
      promotion_hint_aggregator_(
          promotion_hint_aggregator
              ? std::move(promotion_hint_aggregator)
              : std::make_unique<PromotionHintAggregatorImpl>()),
      tick_clock_(tick_clock ? tick_clock
                             : base::DefaultTickClock::GetInstance()) {
  surface_chooser_state_.is_required = is_overlay_required_;
  surface_chooser_state_.promote_secure_only = promote_secure_only;
  surface_chooser_state_.always_use_texture_owner = always_use_texture_owner;
}

SurfaceChooserHelper::~SurfaceChooserHelper() {}

void SurfaceChooserHelper::SetSecureSurfaceMode(SecureSurfaceMode mode) {
  bool is_secure = false;
  requires_secure_video_surface_ = false;

  switch (mode) {
    case SecureSurfaceMode::kInsecure:
      break;
    case SecureSurfaceMode::kRequested:
      is_secure = true;
      break;
    case SecureSurfaceMode::kRequired:
      is_secure = true;
      requires_secure_video_surface_ = true;
      break;
  }

  surface_chooser_state_.is_secure = is_secure;
  surface_chooser_state_.is_required =
      requires_secure_video_surface_ || is_overlay_required_;
}

void SurfaceChooserHelper::SetIsFullscreen(bool is_fullscreen) {
  // TODO(liberato): AVDA previously only set is_expecting_relayout when
  // getting overlay info, not when checking fullscreen for the first time.
  // This might affect pre-M devices.  I think the pre-M path doesn't care.
  if (is_fullscreen && !surface_chooser_state_.is_fullscreen) {
    // It would be nice if we could just delay until we get a hint from an
    // overlay that's "in fullscreen" in the sense that the CompositorFrame it
    // came from had some flag set to indicate that the renderer was in
    // fullscreen mode when it was generated.  However, even that's hard, since
    // there's no real connection between "renderer finds out about fullscreen"
    // and "blink has completed layouts for it".  The latter is what we really
    // want to know.
    surface_chooser_state_.is_expecting_relayout = true;
    hints_until_clear_relayout_flag_ = kFrameDelayForFullscreenLayout;
  }

  surface_chooser_state_.is_fullscreen = is_fullscreen;
}

void SurfaceChooserHelper::SetVideoRotation(VideoRotation video_rotation) {
  surface_chooser_state_.video_rotation = video_rotation;
}

void SurfaceChooserHelper::SetIsPersistentVideo(bool is_persistent_video) {
  surface_chooser_state_.is_persistent_video = is_persistent_video;
}

void SurfaceChooserHelper::UpdateChooserState(
    std::optional<AndroidOverlayFactoryCB> new_factory) {
  surface_chooser_->UpdateState(std::move(new_factory), surface_chooser_state_);
}

void SurfaceChooserHelper::NotifyPromotionHintAndUpdateChooser(
    const PromotionHintAggregator::Hint& hint,
    bool is_using_overlay) {
  bool update_state = false;

  promotion_hint_aggregator_->NotifyPromotionHint(hint);

  // If we're expecting a full screen relayout, then also use this hint as a
  // notification that another frame has happened.
  if (hints_until_clear_relayout_flag_ > 0) {
    hints_until_clear_relayout_flag_--;
    if (hints_until_clear_relayout_flag_ == 0) {
      surface_chooser_state_.is_expecting_relayout = false;
      update_state = true;
    }
  }

  surface_chooser_state_.initial_position = hint.screen_rect;
  bool promotable = promotion_hint_aggregator_->IsSafeToPromote();
  if (promotable != surface_chooser_state_.is_compositor_promotable) {
    surface_chooser_state_.is_compositor_promotable = promotable;
    update_state = true;
  }

  // If we've been provided with enough new frames, then update the state even
  // if it hasn't changed.  This lets |surface_chooser_| retry for an overlay.
  // It's especially helpful for power-efficient overlays, since we don't know
  // when an overlay becomes power efficient.  It also helps retry any failure
  // that's not accompanied by a state change, such as if android destroys the
  // overlay asynchronously for a transient reason.
  //
  // If we're already using an overlay, then there's no need to do this.
  base::TimeTicks now = tick_clock_->NowTicks();
  if (!is_using_overlay &&
      now - most_recent_chooser_retry_ >= RetryChooserTimeout) {
    update_state = true;
  }

  if (update_state) {
    most_recent_chooser_retry_ = now;
    UpdateChooserState(std::optional<AndroidOverlayFactoryCB>());
  }
}

SurfaceChooserHelper::FrameInformation
SurfaceChooserHelper::ComputeFrameInformation(bool is_using_overlay) {
  if (!is_using_overlay) {
    // Not an overlay.
    return surface_chooser_state_.is_secure
               ? FrameInformation::NON_OVERLAY_L3
               : FrameInformation::NON_OVERLAY_INSECURE;
  }

  // Overlay.
  if (surface_chooser_state_.is_secure) {
    return surface_chooser_state_.is_required ? FrameInformation::OVERLAY_L1
                                              : FrameInformation::OVERLAY_L3;
  }

  return surface_chooser_state_.is_fullscreen
             ? FrameInformation::OVERLAY_INSECURE_PLAYER_ELEMENT_FULLSCREEN
             : FrameInformation::OVERLAY_INSECURE_NON_PLAYER_ELEMENT_FULLSCREEN;
}

}  // namespace media