diff --git a/src/debug_utils.h b/src/debug_utils.h
index d738ceedee96e402ccf509215d92a7b69f2f65a8..df93966505293afc09af75d3bcb03a3c0835e1c6 100644
--- a/src/debug_utils.h
+++ b/src/debug_utils.h
@@ -24,8 +24,11 @@
 
 #include "audio/resampler.h"
 #include "libav_deps.h"
+#include "media_io_handle.h"
+#include "system_codec_container.h"
 
 #include <chrono>
+#include <cstdio>
 #include <fstream>
 #include <ios>
 #include <ratio>
@@ -176,4 +179,63 @@ private:
     std::unique_ptr<Resampler> resampler_; // convert from float to integer samples
 };
 
+/**
+ * Minimally invasive video writer. Writes raw frames. Helps debug what goes wrong with video.
+ */
+class VideoWriter {
+public:
+    VideoWriter(const std::string& filename, AVPixelFormat format, int width, int height)
+        : filename_(filename)
+        , format_(format)
+        , width_(width)
+        , height_(height)
+    {
+        f_ = fopen(filename.c_str(), "wb");
+    }
+
+    // so an int (VideoFrame.format()) can be passed without casting
+    VideoWriter(const std::string& filename, int format, int width, int height)
+        : VideoWriter(filename, static_cast<AVPixelFormat>(format), width, height)
+    {}
+
+    ~VideoWriter()
+    {
+        fclose(f_);
+        RING_DBG("Play video file with: ffplay -f rawvideo -pixel_format %s -video_size %dx%d %s",
+            av_get_pix_fmt_name(format_), width_, height_, filename_.c_str());
+    }
+
+    void write(VideoFrame& frame)
+    {
+        int ret = 0;
+        uint8_t* buffer = nullptr;
+        auto f = frame.pointer();
+
+        if (format_ != f->format || width_ != f->width || height_ != f->height)
+            return;
+
+        int size = av_image_get_buffer_size(format_, width_, height_, 1);
+        buffer = reinterpret_cast<uint8_t*>(av_malloc(size));
+        if (!buffer) {
+            return;
+        }
+        if ((ret = av_image_copy_to_buffer(buffer, size,
+            reinterpret_cast<const uint8_t* const*>(f->data),
+            reinterpret_cast<const int*>(f->linesize),
+            format_, width_, height_, 1)) < 0) {
+            av_freep(&buffer);
+            return;
+        }
+
+        fwrite(buffer, 1, size, f_);
+        av_freep(&buffer);
+    }
+
+private:
+    FILE* f_;
+    std::string filename_;
+    AVPixelFormat format_;
+    int width_, height_;
+};
+
 }} // namespace ring::debug