framereader: use the same alignement as visionbuf
diff --git a/selfdrive/ui/replay/framereader.cc b/selfdrive/ui/replay/framereader.cc
index 32af922f1f4aa2..5955d1bfcb05a3 100644
--- a/selfdrive/ui/replay/framereader.cc
+++ b/selfdrive/ui/replay/framereader.cc
@@ -3,6 +3,8 @@
 #include <cassert>
 #include "libyuv.h"
 
+#include "cereal/visionipc/visionbuf.h"
+
 namespace {
 
 struct buffer_data {
@@ -98,6 +100,7 @@ bool FrameReader::load(const std::byte *data, size_t size, bool no_cuda, std::at
 
   width = (decoder_ctx->width + 3) & ~3;
   height = decoder_ctx->height;
+  visionbuf_compute_aligned_width_and_height(width, height, &aligned_width, &aligned_height);
 
   if (has_cuda_device && !no_cuda) {
     if (!initHardwareDecoder(AV_HWDEVICE_TYPE_CUDA)) {
@@ -218,19 +221,21 @@ bool FrameReader::copyBuffers(AVFrame *f, uint8_t *rgb, uint8_t *yuv) {
     libyuv::NV12ToI420(f->data[0], f->linesize[0], f->data[1], f->linesize[1],
                        y, width, u, width / 2, v, width / 2, width, height);
     libyuv::I420ToRGB24(y, width, u, width / 2, v, width / 2,
-                        rgb, width * 3, width, height);
+                        rgb, aligned_width * 3, width, height);
   } else {
     if (yuv) {
       uint8_t *u = yuv + width * height;
       uint8_t *v = u + (width / 2) * (height / 2);
-      memcpy(yuv, f->data[0], width * height);
-      memcpy(u, f->data[1], width / 2 * height / 2);
-      memcpy(v, f->data[2], width / 2 * height / 2);
+      libyuv::I420Copy(f->data[0], f->linesize[0],
+                       f->data[1], f->linesize[1],
+                       f->data[2], f->linesize[2],
+                       yuv, width, u, width / 2, v, width / 2,
+                       width, height);
     }
     libyuv::I420ToRGB24(f->data[0], f->linesize[0],
                         f->data[1], f->linesize[1],
                         f->data[2], f->linesize[2],
-                        rgb, width * 3, width, height);
+                        rgb, aligned_width * 3, width, height);
   }
   return true;
 }
diff --git a/selfdrive/ui/replay/framereader.h b/selfdrive/ui/replay/framereader.h
index d572b727e53828..8de5c1f93eb5cb 100644
--- a/selfdrive/ui/replay/framereader.h
+++ b/selfdrive/ui/replay/framereader.h
@@ -22,12 +22,13 @@ class FrameReader {
   bool load(const std::string &url, bool no_cuda = false, std::atomic<bool> *abort = nullptr, bool local_cache = false, int chunk_size = -1, int retries = 0);
   bool load(const std::byte *data, size_t size, bool no_cuda = false, std::atomic<bool> *abort = nullptr);
   bool get(int idx, uint8_t *rgb, uint8_t *yuv);
-  int getRGBSize() const { return width * height * 3; }
+  int getRGBSize() const { return aligned_width * aligned_height * 3; }
   int getYUVSize() const { return width * height * 3 / 2; }
   size_t getFrameCount() const { return packets.size(); }
   bool valid() const { return valid_; }
 
   int width = 0, height = 0;
+  int aligned_width = 0, aligned_height = 0;
 
 private:
   bool initHardwareDecoder(AVHWDeviceType hw_device_type);

follow up of https://github.com/commaai/openpilot/pull/23200#event-5757453814