chromium/media/gpu/v4l2/v4l2_vp9_helpers.cc

// Copyright 2019 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/v4l2/v4l2_vp9_helpers.h"

#include "base/containers/heap_array.h"
#include "base/logging.h"

namespace media {
namespace {
// Creates superframe index from |frame_sizes|. The frame sizes is stored in the
// same bytes. For example, if the max frame size is two bytes, even if the
// smaller frame sizes are 1 byte, they are stored as two bytes. See the detail
// for VP9 Spec Annex B.
std::vector<uint8_t> CreateSuperFrameIndex(
    const std::vector<uint32_t>& frame_sizes) {
  if (frame_sizes.size() < 2) {
    return {};
  }

  // Computes the bytes of the maximum frame size.
  const uint32_t max_frame_size =
      *std::max_element(frame_sizes.begin(), frame_sizes.end());
  uint8_t bytes_per_framesize = 1;
  for (uint32_t mask = 0xff; bytes_per_framesize <= 4; bytes_per_framesize++) {
    if (max_frame_size < mask) {
      break;
    }
    mask <<= 8;
    mask |= 0xff;
  }

  uint8_t superframe_header = 0xc0;
  superframe_header |= static_cast<uint8_t>(frame_sizes.size() - 1);
  superframe_header |= (bytes_per_framesize - 1) << 3;
  const size_t index_sz = 2 + bytes_per_framesize * frame_sizes.size();
  std::vector<uint8_t> superframe_index(index_sz);
  size_t pos = 0;
  superframe_index[pos++] = superframe_header;
  for (uint32_t size : frame_sizes) {
    for (int i = 0; i < bytes_per_framesize; i++) {
      superframe_index[pos++] = size & 0xff;
      size >>= 8;
    }
  }
  superframe_index[pos++] = superframe_header;

  return superframe_index;
}

// Overwrites show_frame of each frame. It is set to 1 for the top spatial layer
// or otherwise 0.
bool OverwriteShowFrame(base::span<uint8_t> frame_data,
                        const std::vector<uint32_t>& frame_sizes) {
  size_t sum_frame_size = 0;
  for (uint32_t frame_size : frame_sizes) {
    sum_frame_size += frame_size;
  }
  if (frame_data.size() != sum_frame_size) {
    LOG(ERROR) << "frame data size=" << frame_data.size()
               << " is different from the sum of frame sizes"
               << " index size=" << sum_frame_size;
    return false;
  }

  size_t offset = 0;
  for (size_t i = 0; i < frame_sizes.size(); ++i) {
    uint8_t* header = frame_data.data() + offset;

    // See VP9 Spec Annex B.
    const uint8_t frame_marker = (*header >> 6);
    if (frame_marker != 0b10) {
      LOG(ERROR) << "Invalid frame marker: " << static_cast<int>(frame_marker);
      return false;
    }
    const uint8_t profile = (*header >> 4) & 0b11;
    if (profile == 3) {
      LOG(ERROR) << "Unsupported profile";
      return false;
    }

    const bool show_existing_frame = (*header >> 3) & 1;
    const bool show_frame = i == frame_sizes.size() - 1;
    int bit = 0;
    if (show_existing_frame) {
      header++;
      bit = 6;
    } else {
      bit = 1;
    }
    if (show_frame) {
      *header |= (1u << bit);
    } else {
      *header &= ~(1u << bit);
    }

    offset += frame_sizes[i];
  }

  return true;
}
}  // namespace

bool AppendVP9SuperFrameIndex(scoped_refptr<DecoderBuffer>& buffer) {
  DCHECK(buffer->has_side_data());
  DCHECK(!buffer->side_data()->spatial_layers.empty());

  const size_t num_of_layers = buffer->side_data()->spatial_layers.size();
  if (num_of_layers > 3u) {
    LOG(ERROR) << "The maximum number of spatial layers in VP9 is three";
    return false;
  }

  const uint32_t* cue_data = buffer->side_data()->spatial_layers.data();
  std::vector<uint32_t> frame_sizes(cue_data, cue_data + num_of_layers);
  std::vector<uint8_t> superframe_index = CreateSuperFrameIndex(frame_sizes);
  const size_t vp9_superframe_size = buffer->size() + superframe_index.size();
  auto vp9_superframe = base::HeapArray<uint8_t>::Uninit(vp9_superframe_size);
  memcpy(vp9_superframe.data(), buffer->data(), buffer->size());
  memcpy(vp9_superframe.data() + buffer->size(), superframe_index.data(),
         superframe_index.size());

  if (!OverwriteShowFrame(vp9_superframe, frame_sizes)) {
    return false;
  }

  DVLOG(3) << "DecoderBuffer is overwritten";
  buffer = DecoderBuffer::FromArray(std::move(vp9_superframe));

  return true;
}
}  // namespace media