diff --git a/src/client/videomanager.cpp b/src/client/videomanager.cpp
index 9143ba12a95003ba03ff9d10cd4ebf5e40eff00c..7875cbb2c6c76532dc3a53f146fe3c0f8fc05ca8 100644
--- a/src/client/videomanager.cpp
+++ b/src/client/videomanager.cpp
@@ -493,6 +493,15 @@ registerSinkTarget(const std::string& sinkId, const SinkTarget& target)
        RING_WARN("No sink found for id '%s'", sinkId.c_str());
 }
 
+void
+registerAVSinkTarget(const std::string& sinkId, const AVSinkTarget& target)
+{
+   if (auto sink = ring::Manager::instance().getSinkClient(sinkId))
+       sink->registerAVTarget(target);
+   else
+       RING_WARN("No sink found for id '%s'", sinkId.c_str());
+}
+
 std::map<std::string, std::string>
 getRenderer(const std::string& callId)
 {
diff --git a/src/dring/videomanager_interface.h b/src/dring/videomanager_interface.h
index 44bb0d144bc8688056bd3e70a8e042c2db472125..2fdf7ae0f3ad2e57ceccddcb53e639c5e3b20842 100644
--- a/src/dring/videomanager_interface.h
+++ b/src/dring/videomanager_interface.h
@@ -151,6 +151,11 @@ private:
     void setGeometry(int format, int width, int height) noexcept;
 };
 
+struct DRING_PUBLIC AVSinkTarget {
+    std::function<void(std::unique_ptr<VideoFrame>)> push;
+    int /* AVPixelFormat */ preferredFormat {-1 /* AV_PIX_FMT_NONE */};
+};
+
 using VideoCapabilities = std::map<std::string, std::map<std::string, std::vector<std::string>>>;
 
 DRING_PUBLIC std::vector<std::string> getDeviceList();
@@ -171,6 +176,7 @@ DRING_PUBLIC void stopAudioDevice();
 DRING_PUBLIC bool switchInput(const std::string& resource);
 DRING_PUBLIC bool switchToCamera();
 DRING_PUBLIC void registerSinkTarget(const std::string& sinkId, const SinkTarget& target);
+DRING_PUBLIC void registerAVSinkTarget(const std::string& sinkId, const AVSinkTarget& target);
 DRING_PUBLIC std::map<std::string, std::string> getRenderer(const std::string& callId);
 
 DRING_PUBLIC std::string startLocalRecorder(const bool& audioOnly, const std::string& filepath);
diff --git a/src/media/video/sinkclient.cpp b/src/media/video/sinkclient.cpp
index 466c2f2798d77f172b0fc5a96cdafc25f5853f44..ae3d37c93345c6119f2e031e46b80098ee0b42e4 100644
--- a/src/media/video/sinkclient.cpp
+++ b/src/media/video/sinkclient.cpp
@@ -336,9 +336,13 @@ SinkClient::update(Observable<std::shared_ptr<MediaFrame>>* /*obs*/,
     shm_->renderFrame(f);
 #endif
 
+    if (avTarget_.push) {
+        auto outFrame = std::make_unique<VideoFrame>();
+        outFrame->copyFrom(f);
+        avTarget_.push(std::move(outFrame));
+    }
     if (target_.pull) {
         VideoFrame dst;
-        VideoScaler scaler;
         const int width = f.width();
         const int height = f.height();
 #if defined(__ANDROID__) || (defined(__APPLE__) && !TARGET_OS_IPHONE)
diff --git a/src/media/video/sinkclient.h b/src/media/video/sinkclient.h
index 839e5e845eb91c1f4d2f32d5a18d00c2d65f4c9d..5d76751523bef426b683c79633b494bb0e584f04 100644
--- a/src/media/video/sinkclient.h
+++ b/src/media/video/sinkclient.h
@@ -62,6 +62,10 @@ class SinkClient : public VideoFramePassiveReader
             return height_;
         }
 
+        AVPixelFormat getPreferredFormat() const noexcept {
+            return (AVPixelFormat)avTarget_.preferredFormat;
+        }
+
         // as VideoFramePassiveReader
         void update(Observable<std::shared_ptr<ring::MediaFrame>>*,
                     const std::shared_ptr<ring::MediaFrame>&) override;
@@ -74,6 +78,9 @@ class SinkClient : public VideoFramePassiveReader
         void registerTarget(const DRing::SinkTarget& target) noexcept {
             target_ = target;
         }
+        void registerAVTarget(const DRing::AVSinkTarget& target) noexcept {
+            avTarget_ = target;
+        }
 
     private:
         const std::string id_;
@@ -82,6 +89,7 @@ class SinkClient : public VideoFramePassiveReader
         int height_ {0};
         bool started_ {false}; // used to arbitrate client's stop signal.
         DRing::SinkTarget target_;
+        DRing::AVSinkTarget avTarget_;
         std::unique_ptr<VideoScaler> scaler_;
 
 #ifdef DEBUG_FPS