From db53fb79527decd9d5c80a67efc60d9c12488e06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20R=C3=B6ger?= Date: Thu, 19 Mar 2026 00:27:07 +0100 Subject: [PATCH] feat(avpipe): add SmearFrames --- app/mosh-pipe.cpp | 2 +- include/mosh-me/avpipe.hpp | 23 +++++++++++++++ src/avpipe.cpp | 60 +++++++++++++++++++++++++++++++++++++- 3 files changed, 83 insertions(+), 2 deletions(-) diff --git a/app/mosh-pipe.cpp b/app/mosh-pipe.cpp index 67f9d43..bba5766 100644 --- a/app/mosh-pipe.cpp +++ b/app/mosh-pipe.cpp @@ -15,7 +15,7 @@ int main(int argc, char *argv[]) { try { auto pipe = mosh_me::VideoMuxer::link(std::filesystem::path(argv[2])) - << mosh_me::DropIFrames::link() + << mosh_me::SmearFrames::link(3, 5) << mosh_me::MoshableVideoEncoder::link() << mosh_me::VideoDecoder::link() << mosh_me::VideoPacketSource::link(argv[1]); diff --git a/include/mosh-me/avpipe.hpp b/include/mosh-me/avpipe.hpp index 3211159..944a810 100644 --- a/include/mosh-me/avpipe.hpp +++ b/include/mosh-me/avpipe.hpp @@ -60,7 +60,30 @@ class DropIFrames std::pair, FrameMeta>, std::pair, FrameMeta>> { +private: + uint32_t remaining_ = 0; + public: + DropIFrames(uint32_t keep_n = 0); + mosh_me::PipeData pull() noexcept override; + std::pair, FrameMeta> + propagateMeta() noexcept override; +}; + +class SmearFrames + : public AsPipeLink< + SmearFrames, av::Packet, av::Packet, + std::pair, FrameMeta>, + std::pair, + FrameMeta>> { +private: + std::vector back_buffer_; + std::vector::iterator read_head_; + uint32_t n_repeat_ = 0; + uint32_t current_ix_ = 0; + +public: + SmearFrames(uint32_t n_frames = 1, uint32_t n_repeat = 2); mosh_me::PipeData pull() noexcept override; std::pair, FrameMeta> propagateMeta() noexcept override; diff --git a/src/avpipe.cpp b/src/avpipe.cpp index be5df93..9e1e79f 100644 --- a/src/avpipe.cpp +++ b/src/avpipe.cpp @@ -117,6 +117,8 @@ mosh_me::MoshableVideoEncoder::propagateMeta() noexcept { return {ctx_, frame_meta}; } +mosh_me::DropIFrames::DropIFrames(uint32_t keep_n) : remaining_(keep_n) {} + mosh_me::PipeData mosh_me::DropIFrames::pull() noexcept { for (;;) { auto input = fetchInput(); @@ -124,8 +126,10 @@ mosh_me::PipeData mosh_me::DropIFrames::pull() noexcept { return std::unexpected(input.error()); } - if (input->isKeyPacket()) { + if (input->isKeyPacket() && remaining_ == 0) { continue; + } else if (input->isKeyPacket()) { + remaining_--; } return input; @@ -144,6 +148,60 @@ mosh_me::VideoMuxer::VideoMuxer(const std::filesystem::path &path) { ctx_.openOutput(path); } +mosh_me::SmearFrames::SmearFrames(uint32_t n_frames, uint32_t n_repeat) + : n_repeat_(n_repeat) { + back_buffer_.reserve(n_frames); + read_head_ = back_buffer_.begin(); +} + +mosh_me::PipeData mosh_me::SmearFrames::pull() noexcept { + for (;;) { + auto input = fetchInput(); + if (!input) { + // TODO: dump back_buffer + return std::unexpected(input.error()); + } + + // Do not repeat iframes + if (input->isKeyPacket()) { + current_ix_ = 0; + back_buffer_.clear(); + read_head_ = back_buffer_.begin(); + return input; + } + + if (back_buffer_.size() == back_buffer_.capacity()) { + // Full, dump the back buffer + if (read_head_ == back_buffer_.end() && current_ix_ == n_repeat_ - 1) { + current_ix_ = 0; + back_buffer_.clear(); + read_head_ = back_buffer_.begin(); + } else if (read_head_ == back_buffer_.end()) { + current_ix_++; + read_head_ = back_buffer_.begin(); + auto frame = *read_head_++; + frame.setDts(input->dts()); + frame.setPts(input->pts()); + return frame; + } else { + auto frame = *read_head_++; + frame.setDts(input->dts()); + frame.setPts(input->pts()); + return frame; + } + } else { + // Fill back_buffer + back_buffer_.push_back(*input); + } + } +} + +std::pair, mosh_me::FrameMeta> + +mosh_me::SmearFrames::propagateMeta() noexcept { + return fetchMeta(); +} + mosh_me::PipeData mosh_me::VideoMuxer::pull() noexcept { std::error_code ec;