910e62b5创建于 1月15日历史提交
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "remoting/test/video_frame_writer.h"

#include <stdint.h>

#include <vector>

#include "base/compiler_specific.h"
#include "base/containers/span.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/i18n/time_formatting.h"
#include "base/logging.h"
#include "base/strings/stringprintf.h"
#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
#include "ui/gfx/codec/png_codec.h"
#include "ui/gfx/geometry/size.h"

namespace {

const base::FilePath::CharType kFrameFileName[] =
    FILE_PATH_LITERAL("frame.png");
const base::FilePath::CharType kRemotingFolder[] =
    FILE_PATH_LITERAL("remoting");
const base::FilePath::CharType kDumpFrameFolder[] =
    FILE_PATH_LITERAL("dumped_images");

}  // namespace

namespace remoting::test {

VideoFrameWriter::VideoFrameWriter()
    : instance_creation_time_(base::Time::Now()),
      frame_name_unique_number_(0) {}

VideoFrameWriter::~VideoFrameWriter() = default;

void VideoFrameWriter::WriteFrameToPath(const webrtc::DesktopFrame& frame,
                                        const base::FilePath& image_path) {
  CHECK_EQ(frame.pixel_format(), webrtc::FOURCC_ARGB);
  uint8_t* frame_data = reinterpret_cast<unsigned char*>(frame.data());

  std::optional<std::vector<uint8_t>> png_encoded_data = gfx::PNGCodec::Encode(
      frame_data, gfx::PNGCodec::FORMAT_BGRA,
      gfx::Size(frame.size().width(), frame.size().height()), frame.stride(),
      true, std::vector<gfx::PNGCodec::Comment>());
  if (!png_encoded_data) {
    LOG(WARNING) << "Failed to encode frame to PNG file";
    return;
  }

  if (!base::WriteFile(image_path, png_encoded_data.value())) {
    LOG(WARNING) << "Failed to write frame to disk";
  }
}

// Save video frame to path named with the |instance_creation_time|.
void VideoFrameWriter::WriteFrameToDefaultPath(
    const webrtc::DesktopFrame& frame) {
  base::FilePath dump_frame_file_path;
  if (!GetTempDir(&dump_frame_file_path)) {
    LOG(WARNING) << "Failed to retrieve temporary directory path";
    return;
  }

  dump_frame_file_path = dump_frame_file_path.Append(kRemotingFolder);
  dump_frame_file_path = dump_frame_file_path.Append(kDumpFrameFolder);
  if (!CreateDirectoryIfNotExists(dump_frame_file_path)) {
    return;
  }

  // Create a sub-folder using date and time to identify a particular test run.
  dump_frame_file_path = AppendCreationDateAndTime(dump_frame_file_path);
  if (!CreateDirectoryIfNotExists(dump_frame_file_path)) {
    return;
  }

  dump_frame_file_path = dump_frame_file_path.Append(kFrameFileName);

  dump_frame_file_path = dump_frame_file_path.InsertBeforeExtensionASCII(
      base::StringPrintf("(%d)", ++frame_name_unique_number_));

  LOG(INFO) << "Video frame dumped to: " << dump_frame_file_path.value();

  WriteFrameToPath(frame, dump_frame_file_path);
}

void VideoFrameWriter::HighlightRectInFrame(webrtc::DesktopFrame* frame,
                                            const webrtc::DesktopRect& rect) {
  if (rect.left() < 0 || rect.top() < 0 ||
      rect.right() >= frame->size().width() ||
      rect.bottom() >= frame->size().height()) {
    LOG(ERROR) << "Highlight rect lies outside of the frame.";
    return;
  }

  // Draw vertical borders.
  for (int y = rect.top(); y <= rect.bottom(); ++y) {
    ShiftPixelColor(frame, rect.left(), y, 128);
    ShiftPixelColor(frame, rect.right(), y, 128);
  }

  // Draw horizontal borders.
  for (int x = rect.left(); x <= rect.right(); ++x) {
    ShiftPixelColor(frame, x, rect.top(), 128);
    ShiftPixelColor(frame, x, rect.bottom(), 128);
  }
}

base::FilePath VideoFrameWriter::AppendCreationDateAndTime(
    const base::FilePath& file_path) {
  return file_path.AppendASCII(base::UnlocalizedTimeFormatWithPattern(
      instance_creation_time_, "y-M-d_H-m-s"));
}

bool VideoFrameWriter::CreateDirectoryIfNotExists(
    const base::FilePath& file_path) {
  if (!base::DirectoryExists(file_path) && !base::CreateDirectory(file_path)) {
    LOG(WARNING) << "Failed to create directory: " << file_path.value();
    return false;
  }
  return true;
}

void VideoFrameWriter::ShiftPixelColor(webrtc::DesktopFrame* frame,
                                       int x,
                                       int y,
                                       int shift_amount) {
  // SAFETY: No safe interface to `webrtc::DesktopFrame`.
  UNSAFE_BUFFERS(base::span<uint8_t> pixel(
      frame->data() + y * frame->stride() +
          x * webrtc::DesktopFrame::kBytesPerPixel,
      static_cast<size_t>(webrtc::DesktopFrame::kBytesPerPixel)));

  // Only shift RGB channels.
  for (int i = 0; i < 3; ++i) {
    pixel[i] += shift_amount;
  }
}

}  // namespace remoting::test