blob: b268fa4622d1c67d5ad7bbc0618c4a424ee9631a [file] [log] [blame] [edit]
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "faced/camera/frame_utils.h"
#include <memory>
#include <string>
#include <string_view>
#include <base/strings/strcat.h>
#include <linux/videodev2.h>
#include "faced/camera/frame.h"
namespace faced {
namespace {
// Stride is unused if the stride value is 0 or equal to width
bool IsStrideUnused(int stride, int width) {
return stride == 0 || stride == width;
}
} // namespace
std::string GetTightlyPackedPayload(int height,
int width,
const cros_cam_plane_t_& plane_y,
const cros_cam_plane_t_& plane_uv) {
// If the stride values are unused then no further processing needs to be
// done.
if (IsStrideUnused(plane_y.stride, width) &&
IsStrideUnused(plane_uv.stride, width)) {
return base::StrCat({
std::string_view(reinterpret_cast<const char*>(plane_y.data),
plane_y.size),
std::string_view(reinterpret_cast<const char*>(plane_uv.data),
plane_uv.size),
});
}
// The expected size is equal to a full size luminance (Y) component of size
// (height * width) with a 2x2 subsampled chroma (UV) component of size
// (height + 1) / 2 * width
int expected_size = height * width + (height + 1) / 2 * width;
std::string payload = std::string(expected_size, 0);
// First copy the luminance (Y) values
// Keep track of pointers to the start of each source and destination row
uint8_t* src_y = plane_y.data;
char* dst = payload.data();
// If the stride value is unused, then the luminance data is already tightly
// packed.
if (IsStrideUnused(plane_y.stride, width)) {
memcpy(dst, src_y, height * width);
dst += height * width;
} else {
for (int i = 0; i < height; i++) {
memcpy(dst, src_y, width);
src_y += plane_y.stride;
dst += width;
}
}
// Next copy the subsampled chroma (UV) values
uint8_t* src_uv = plane_uv.data;
// If the stride value is unused, then the chroma data is already tightly
// packed.
if (IsStrideUnused(plane_uv.stride, width)) {
memcpy(dst, src_uv, (height + 1) / 2 * width);
} else {
for (int i = 0; i < (height + 1) / 2; i++) {
memcpy(dst, src_uv, width);
src_uv += plane_uv.stride;
dst += width;
}
}
return payload;
}
std::unique_ptr<Frame> FrameFromCrosFrame(const cros_cam_frame_t& frame) {
CHECK_GE(frame.format.height, 0);
CHECK_GE(frame.format.width, 0);
// Create the frame.
auto result = std::make_unique<Frame>(Frame{
.height = static_cast<uint32_t>(frame.format.height),
.width = static_cast<uint32_t>(frame.format.width),
});
// Copy and process the payload.
switch (frame.format.fourcc) {
case V4L2_PIX_FMT_NV12:
result->format = Frame::Format::kYuvNv12;
result->data =
GetTightlyPackedPayload(frame.format.height, frame.format.width,
frame.planes[0], frame.planes[1]);
break;
case V4L2_PIX_FMT_MJPEG:
result->format = Frame::Format::kMjpeg;
result->data = std::string(reinterpret_cast<char*>(frame.planes[0].data),
frame.planes[0].size);
break;
}
return result;
}
} // namespace faced