blob: a7e39ff6583088b55f44112cd8031190d7694542 [file] [log] [blame]
// 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