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
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196
  197
  198
  199
  200
  201
  202
  203
  204
  205
  206
  207
  208
  209
  210
  211
  212
  213
  214
  215
  216
  217
  218
  219
  220
  221
  222
  223
  224
  225
  226
  227
  228
  229
  230
  231
  232
  233

media / gpu / gpu_video_encode_accelerator_helpers.cc [blame]

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

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif

#include "media/gpu/gpu_video_encode_accelerator_helpers.h"

#include <algorithm>
#include <ostream>

#include "base/check_op.h"
#include "base/notreached.h"
#include "base/numerics/safe_conversions.h"
#include "media/base/bitrate.h"

namespace media {
namespace {
// The maximum number of supported spatial layers and temporal layers. These
// come from the maximum number of layers currently supported by
// VideoEncodeAccelerator implementation.
constexpr size_t kMaxSpatialLayers = 3;
constexpr size_t kMaxTemporalLayers = 3;

// The maximum size for output buffer, which is chosen empirically for
// 1080p video.
constexpr size_t kMaxBitstreamBufferSizeInBytes = 2 * 1024 * 1024;  // 2MB

// The frame size for 1080p (FHD) video in pixels.
constexpr int k1080PSizeInPixels = 1920 * 1080;
// The frame size for 1440p (QHD) video in pixels.
constexpr int k1440PSizeInPixels = 2560 * 1440;

// The mapping from resolution, bitrate, framerate to the bitstream buffer size.
struct BitstreamBufferSizeInfo {
  int coded_size_area;
  uint32_t bitrate_in_bps;
  uint32_t framerate;
  uint32_t buffer_size_in_bytes;
};

// The bitstream buffer size for each resolution. The table must be sorted in
// increasing order by the resolution. The value is decided by measuring the
// biggest buffer size, and then double the size as margin. (crbug.com/889739)
constexpr BitstreamBufferSizeInfo kBitstreamBufferSizeTable[] = {
    {320 * 180, 100000, 30, 15000},
    {640 * 360, 500000, 30, 52000},
    {1280 * 720, 1200000, 30, 110000},
    {1920 * 1080, 4000000, 30, 380000},
    {3840 * 2160, 20000000, 30, 970000},
};

// Use quadruple size of kMaxBitstreamBufferSizeInBytes when the input frame
// size is larger than 1440p, double if larger than 1080p. This is chosen
// empirically for some 4k encoding use cases and Android CTS VideoEncoderTest
// (crbug.com/927284).
size_t GetMaxEncodeBitstreamBufferSize(const gfx::Size& size) {
  if (size.GetArea() > k1440PSizeInPixels)
    return kMaxBitstreamBufferSizeInBytes * 4;
  if (size.GetArea() > k1080PSizeInPixels)
    return kMaxBitstreamBufferSizeInBytes * 2;
  return kMaxBitstreamBufferSizeInBytes;
}
}  // namespace

// This function sets the peak equal to the target. The peak can then be
// updated by callers.
VideoBitrateAllocation AllocateBitrateForDefaultEncodingWithBitrates(
    const std::vector<uint32_t>& sl_bitrates,
    const size_t num_temporal_layers,
    const bool uses_vbr) {
  CHECK(!sl_bitrates.empty());
  CHECK_LE(sl_bitrates.size(), kMaxSpatialLayers);

  // The same bitrate factors as the software encoder.
  // https://source.chromium.org/chromium/chromium/src/+/main:media/video/vpx_video_encoder.cc;l=131;drc=d383d0b3e4f76789a6de2a221c61d3531f4c59da
  constexpr double kTemporalLayersBitrateScaleFactors[][kMaxTemporalLayers] = {
      {1.00, 0.00, 0.00},  // For one temporal layer.
      {0.60, 0.40, 0.00},  // For two temporal layers.
      {0.50, 0.20, 0.30},  // For three temporal layers.
  };

  CHECK_GT(num_temporal_layers, 0u);
  CHECK_LE(num_temporal_layers, std::size(kTemporalLayersBitrateScaleFactors));
  DCHECK_EQ(std::size(kTemporalLayersBitrateScaleFactors), kMaxTemporalLayers);

  VideoBitrateAllocation bitrate_allocation;
  bitrate_allocation = VideoBitrateAllocation(
      uses_vbr ? Bitrate::Mode::kVariable : Bitrate::Mode::kConstant);
  for (size_t spatial_id = 0; spatial_id < sl_bitrates.size(); ++spatial_id) {
    const uint32_t bitrate_bps = sl_bitrates[spatial_id];
    for (size_t temporal_id = 0; temporal_id < num_temporal_layers;
         ++temporal_id) {
      const double factor =
          kTemporalLayersBitrateScaleFactors[num_temporal_layers - 1]
                                            [temporal_id];
      bitrate_allocation.SetBitrate(
          spatial_id, temporal_id,
          base::saturated_cast<uint32_t>(bitrate_bps * factor));
    }
  }

  return bitrate_allocation;
}

size_t GetEncodeBitstreamBufferSize(const gfx::Size& size,
                                    uint32_t bitrate,
                                    uint32_t framerate) {
  DCHECK_NE(framerate, 0u);
  for (auto& data : kBitstreamBufferSizeTable) {
    if (size.GetArea() <= data.coded_size_area) {
      // The buffer size is proportional to (bitrate / framerate), but linear
      // interpolation for smaller ratio is not enough. Therefore we only use
      // linear extrapolation for larger ratio.
      double ratio = std::max(
          1.0f * (bitrate / framerate) / (data.bitrate_in_bps / data.framerate),
          1.0f);
      return std::min(static_cast<size_t>(data.buffer_size_in_bytes * ratio),
                      GetMaxEncodeBitstreamBufferSize(size));
    }
  }
  return GetMaxEncodeBitstreamBufferSize(size);
}

// Get the maximum output bitstream buffer size. Since we don't change the
// buffer size when we update bitrate and framerate, we have to calculate the
// buffer size for the maximum bitrate.
// However, the maximum bitrate for intel chipset is 40Mbps. The buffer size
// calculated with this bitrate is always larger than 2MB. Therefore we just
// return the value.
// TODO(crbug.com/889739): Deprecate this function after we can update the
// buffer size while requesting new bitrate and framerate.
size_t GetEncodeBitstreamBufferSize(const gfx::Size& size) {
  return GetMaxEncodeBitstreamBufferSize(size);
}

std::vector<uint8_t> GetFpsAllocation(size_t num_temporal_layers) {
  DCHECK_LT(num_temporal_layers, 4u);
  constexpr uint8_t kFullAllocation = 255;
  // The frame rate fraction is given as an 8 bit unsigned integer where 0 = 0%
  // and 255 = 100%. Each layer's allocated fps refers to the previous one, so
  // e.g. your camera is opened at 30fps, and you want to have decode targets at
  // 15fps and 7.5fps as well:
  // TL0 then gets an allocation of 7.5/30 = 1/4. TL1 adds another 7.5fps to end
  // up at (7.5 + 7.5)/30 = 15/30 = 1/2 of the total allocation. TL2 adds the
  // final 15fps to end up at (15 + 15)/30, which is the full allocation.
  // Therefore, fps_allocation values are as follows,
  // fps_allocation[0][0] = kFullAllocation / 4;
  // fps_allocation[0][1] = kFullAllocation / 2;
  // fps_allocation[0][2] = kFullAllocation;
  //  For more information, see webrtc::VideoEncoderInfo::fps_allocation.
  switch (num_temporal_layers) {
    case 1:
      // In this case, the number of spatial layers must great than 1.
      return {kFullAllocation};
    case 2:
      return {kFullAllocation / 2, kFullAllocation};
    case 3:
      return {kFullAllocation / 4, kFullAllocation / 2, kFullAllocation};
    default:
      NOTREACHED() << "Unsupported temporal layers";
  }
}

VideoBitrateAllocation AllocateBitrateForDefaultEncoding(
    const VideoEncodeAccelerator::Config& config) {
  if (config.bitrate.mode() == Bitrate::Mode::kExternal) {
    return VideoBitrateAllocation(Bitrate::Mode::kExternal);
  }

  VideoBitrateAllocation allocation;
  const bool use_vbr = config.bitrate.mode() == Bitrate::Mode::kVariable;
  if (config.spatial_layers.empty()) {
    allocation = AllocateBitrateForDefaultEncodingWithBitrates(
        {config.bitrate.target_bps()},
        /*num_temporal_layers=*/1u, use_vbr);
    if (use_vbr) {
      allocation.SetPeakBps(config.bitrate.peak_bps());
    }
    return allocation;
  }

  const size_t num_temporal_layers =
      config.spatial_layers[0].num_of_temporal_layers;
  std::vector<uint32_t> bitrates;
  bitrates.reserve(config.spatial_layers.size());
  for (const auto& spatial_layer : config.spatial_layers) {
    DCHECK_EQ(spatial_layer.num_of_temporal_layers, num_temporal_layers);
    bitrates.push_back(spatial_layer.bitrate_bps);
  }

  allocation = AllocateBitrateForDefaultEncodingWithBitrates(
      bitrates, num_temporal_layers, use_vbr);
  if (use_vbr) {
    allocation.SetPeakBps(config.bitrate.peak_bps());
  }
  return allocation;
}

VideoBitrateAllocation AllocateDefaultBitrateForTesting(
    const size_t num_spatial_layers,
    const size_t num_temporal_layers,
    const Bitrate& bitrate) {
  // Higher spatial layers (those to the right) get more bitrate.
  constexpr double kSpatialLayersBitrateScaleFactors[][kMaxSpatialLayers] = {
      {1.00, 0.00, 0.00},  // For one spatial layer.
      {0.30, 0.70, 0.00},  // For two spatial layers.
      {0.07, 0.23, 0.70},  // For three spatial layers.
  };

  CHECK_GT(num_spatial_layers, 0u);
  CHECK_LE(num_spatial_layers, std::size(kSpatialLayersBitrateScaleFactors));
  DCHECK_EQ(std::size(kSpatialLayersBitrateScaleFactors), kMaxSpatialLayers);

  std::vector<uint32_t> bitrates(num_spatial_layers);
  for (size_t sid = 0; sid < num_spatial_layers; ++sid) {
    const double bitrate_factor =
        kSpatialLayersBitrateScaleFactors[num_spatial_layers - 1][sid];
    bitrates[sid] = bitrate.target_bps() * bitrate_factor;
  }

  const bool use_vbr = bitrate.mode() == Bitrate::Mode::kVariable;
  auto allocation = AllocateBitrateForDefaultEncodingWithBitrates(
      bitrates, num_temporal_layers, use_vbr);
  if (use_vbr)
    allocation.SetPeakBps(bitrate.peak_bps());
  return allocation;
}

}  // namespace media