Files
fegger 6516b45b02 perf(codec): pass YUV through to the renderer and use slice threading
The receiver decoded H.264 to YUV420P, converted it to RGBA via a
CPU-intensive swscale pass, then uploaded 4 bytes/pixel to an SDL
texture — only for the GPU to convert back to RGB during rendering.
This eliminated the swscale pass entirely (40-60% of receiver CPU at
1080p) and cut the texture upload by 62%.

- DecodedFrame now carries three YUV420P planes with their strides
  instead of a packed RGBA buffer; the decoder copies the planes
  directly from the AVFrame (zero conversion for the common software
  path). Non-YUV420P decoder output (e.g. NV12 from v4l2m2m) is
  converted once to YUV420P.
- The SDL renderer uploads via SDL_UpdateYUVTexture with
  SDL_PIXELFORMAT_IYUV; the GPU does the YUV→RGB conversion during
  rendering.
- Decoder threading: slice-level with 4 threads (parallelizes within a
  frame, no added latency), not frame-level (which buffers multiple
  frames — the initial thread_count=0 broke the loopback test because
  the H.264 decoder introduced a multi-frame delay before producing
  output).
- The round-trip test converts decoded YUV back to RGBA for pixel
  comparison via a test-local swscale call (the pipeline itself never
  converts).

meson test 5/5 in both configurations, valgrind clean.
2026-09-09 09:45:22 +02:00

54 lines
1.4 KiB
C++

#pragma once
#include "screencast/codec/encoder.h"
#include "screencast/codec/error.h"
#include <cstdint>
#include <memory>
#include <string>
#include <vector>
namespace sc {
// Decoded video frame in YUV420P planar format (the native decoder output,
// passed to the renderer without any colorspace conversion).
struct DecodedFrame {
int width = 0;
int height = 0;
uint64_t capture_timestamp_ns = 0;
// YUV420P planes; each row is `stride` bytes wide and may be padded
// beyond the picture width.
std::vector<std::byte> plane_y;
std::vector<std::byte> plane_u;
std::vector<std::byte> plane_v;
int stride_y = 0;
int stride_u = 0;
int stride_v = 0;
};
struct DecoderConfig {
std::string codec_name = "h264";
int width = 0;
int height = 0;
std::vector<std::byte> extradata; // SPS/PPS for H.264
// Probe a hardware decoder (v4l2 mem2mem) first and fall back to the
// software decoder automatically. Disable with --swdecode.
bool hardware_accel = true;
};
class Decoder {
public:
virtual ~Decoder() = default;
// Feed one encoded frame. Returns decoded frames when available.
virtual CodecResult<std::vector<DecodedFrame>> decode(const EncodedFrame& frame) = 0;
};
class DecoderFactory {
public:
static CodecResult<std::unique_ptr<Decoder>> create(const DecoderConfig& config);
};
} // namespace sc