diff --git a/bin/jni/videomanager.i b/bin/jni/videomanager.i
index 1e411953bbf34e2749f49d7bf65b5f801972fbc6..414da8da48d140cad8f9aee185fc0c38514db6ef 100644
--- a/bin/jni/videomanager.i
+++ b/bin/jni/videomanager.i
@@ -57,7 +57,7 @@ public:
 
 %{
 
-std::map<ANativeWindow*, std::unique_ptr<DRing::FrameBuffer>> windows {};
+std::map<ANativeWindow*, DRing::FrameBuffer> windows {};
 std::mutex windows_mutex;
 
 std::vector<uint8_t> workspace;
@@ -159,13 +159,9 @@ JNIEXPORT void JNICALL Java_net_jami_daemon_JamiServiceJNI_captureVideoPacket(JN
         auto frame = DRing::getNewFrame(input);
         if (not frame)
             return;
-        auto packet = std::unique_ptr<AVPacket, void(*)(AVPacket*)>(new AVPacket, [](AVPacket* pkt){
-            if (pkt) {
-                av_packet_unref(pkt);
-                delete pkt;
-            }
+        auto packet = std::unique_ptr<AVPacket, void(*)(AVPacket*)>(av_packet_alloc(), [](AVPacket* pkt){
+            av_packet_free(&pkt);
         });
-        av_init_packet(packet.get());
         if (keyframe)
             packet->flags = AV_PKT_FLAG_KEY;
         setRotation(rotation);
@@ -314,7 +310,7 @@ JNIEXPORT void JNICALL Java_net_jami_daemon_JamiServiceJNI_setNativeWindowGeomet
     ANativeWindow_setBuffersGeometry(window, width, height, WINDOW_FORMAT_RGBX_8888);
 }
 
-void releaseBuffer(ANativeWindow *window, std::unique_ptr<DRing::FrameBuffer> frame)
+void releaseBuffer(ANativeWindow *window, DRing::FrameBuffer frame)
 {
     std::unique_lock<std::mutex> guard(windows_mutex);
     try {
@@ -324,16 +320,16 @@ void releaseBuffer(ANativeWindow *window, std::unique_ptr<DRing::FrameBuffer> fr
     }
 }
 
-void AndroidDisplayCb(ANativeWindow *window, std::unique_ptr<DRing::FrameBuffer> frame)
+void AndroidDisplayCb(ANativeWindow *window, DRing::FrameBuffer frame)
 {
     ANativeWindow_unlockAndPost(window);
     releaseBuffer(window, std::move(frame));
 }
 
-std::unique_ptr<DRing::FrameBuffer> sinkTargetPullCallback(ANativeWindow *window, std::size_t bytes)
+DRing::FrameBuffer sinkTargetPullCallback(ANativeWindow *window)
 {
     try {
-        std::unique_ptr<DRing::FrameBuffer> frame;
+        DRing::FrameBuffer frame;
         {
             std::lock_guard<std::mutex> guard(windows_mutex);
             frame = std::move(windows.at(window));
@@ -341,11 +337,11 @@ std::unique_ptr<DRing::FrameBuffer> sinkTargetPullCallback(ANativeWindow *window
         if (frame) {
             ANativeWindow_Buffer buffer;
             if (ANativeWindow_lock(window, &buffer, nullptr) == 0) {
-                frame->avframe->format = AV_PIX_FMT_RGBA;
-                frame->avframe->width = buffer.width;
-                frame->avframe->height = buffer.height;
-                frame->avframe->data[0] = (uint8_t *) buffer.bits;
-                frame->avframe->linesize[0] = buffer.stride * 4;
+                frame->format = AV_PIX_FMT_RGBA;
+                frame->width = buffer.width;
+                frame->height = buffer.height;
+                frame->data[0] = (uint8_t *) buffer.bits;
+                frame->linesize[0] = buffer.stride * 4;
                 return frame;
             } else {
                 __android_log_print(ANDROID_LOG_WARN, TAG, "Can't lock window");
@@ -371,13 +367,11 @@ JNIEXPORT void JNICALL Java_net_jami_daemon_JamiServiceJNI_registerVideoCallback
 
     ANativeWindow* nativeWindow = (ANativeWindow*)((intptr_t) window);
     auto f_display_cb = std::bind(&AndroidDisplayCb, nativeWindow, std::placeholders::_1);
-    auto p_display_cb = std::bind(&sinkTargetPullCallback, nativeWindow, std::placeholders::_1);
+    auto p_display_cb = std::bind(&sinkTargetPullCallback, nativeWindow);
 
     {
         std::lock_guard<std::mutex> guard(windows_mutex);
-        auto buf = std::make_unique<DRing::FrameBuffer>();
-        buf->avframe.reset(av_frame_alloc());
-        windows.emplace(nativeWindow, std::move(buf));
+        windows.emplace(nativeWindow, av_frame_alloc());
     }
     DRing::registerSinkTarget(sink, DRing::SinkTarget {.pull=p_display_cb, .push=f_display_cb});
 }
diff --git a/src/jami/videomanager_interface.h b/src/jami/videomanager_interface.h
index 2a0d13f4689f4e0efd78ed5e2a50d4477c2b26c3..a53cb16903af451100fc582949190db85c0a755d 100644
--- a/src/jami/videomanager_interface.h
+++ b/src/jami/videomanager_interface.h
@@ -157,26 +157,16 @@ private:
     void setGeometry(int format, int width, int height) noexcept;
 };
 
-/* FrameBuffer is a generic video frame container */
-struct DRING_PUBLIC FrameBuffer
-{
-    uint8_t* ptr {nullptr};  // data as a plain raw pointer
-    std::size_t ptrSize {0}; // size in byte of ptr array
-    int format {0};          // as listed by AVPixelFormat (avutils/pixfmt.h)
-    int width {0};           // frame width
-    int height {0};          // frame height
-    std::vector<uint8_t> storage;
-    // If set, new frame will be written to this buffer instead
-    std::unique_ptr<AVFrame, void (*)(AVFrame*)> avframe {nullptr, [](AVFrame* frame) {
-                                                              av_frame_free(&frame);
-                                                          }};
+struct DRING_PUBLIC AVFrame_deleter {
+    void operator()(AVFrame* frame) const { av_frame_free(&frame); }
 };
 
+typedef std::unique_ptr<AVFrame, AVFrame_deleter> FrameBuffer;
+
 struct DRING_PUBLIC SinkTarget
 {
-    using FrameBufferPtr = std::unique_ptr<FrameBuffer>;
-    std::function<FrameBufferPtr(std::size_t bytes)> pull;
-    std::function<void(FrameBufferPtr)> push;
+    std::function<FrameBuffer()> pull;
+    std::function<void(FrameBuffer)> push;
 };
 
 struct DRING_PUBLIC AVSinkTarget
diff --git a/src/media/media_buffer.h b/src/media/media_buffer.h
index ef287e0576ab682c1dc4a53b8ddbb3ded940f84b..3752d84632f01fa5dcd1e3cab18eb2139e810bda 100644
--- a/src/media/media_buffer.h
+++ b/src/media/media_buffer.h
@@ -27,14 +27,6 @@
 #include <memory>
 #include <functional>
 
-extern "C" {
-struct AVFrame;
-}
-
-namespace DRing {
-struct FrameBuffer; //  from jami/videomanager_interface.h
-}
-
 namespace jami {
 
 using MediaFrame = DRing::MediaFrame;
diff --git a/src/media/video/sinkclient.cpp b/src/media/video/sinkclient.cpp
index 4dcbf2bba5b10472b83ca89a774e43de0917a08f..0195b4ed5eb1dc230bde0c757e3e8b81584fb3a9 100644
--- a/src/media/video/sinkclient.cpp
+++ b/src/media/video/sinkclient.cpp
@@ -438,24 +438,9 @@ SinkClient::update(Observable<std::shared_ptr<MediaFrame>>* /*obs*/,
         if (target_.pull) {
             int width = frame->width();
             int height = frame->height();
-#if defined(__ANDROID__) || (defined(__APPLE__) && !TARGET_OS_IPHONE)
-            const int format = AV_PIX_FMT_RGBA;
-#else
-            const int format = AV_PIX_FMT_BGRA;
-#endif
-            const auto bytes = videoFrameSize(format, width, height);
-            if (bytes > 0) {
-                if (auto buffer_ptr = target_.pull(bytes)) {
-                    if (buffer_ptr->avframe) {
-                        scaler_->scale(*frame, buffer_ptr->avframe.get());
-                    } else {
-                        buffer_ptr->format = format;
-                        buffer_ptr->width = width;
-                        buffer_ptr->height = height;
-                        VideoFrame dst;
-                        dst.setFromMemory(buffer_ptr->ptr, format, width, height);
-                        scaler_->scale(*frame, dst);
-                    }
+            if (width > 0 && height > 0) {
+                if (auto buffer_ptr = target_.pull()) {
+                    scaler_->scale(*frame, buffer_ptr.get());
                     target_.push(std::move(buffer_ptr));
                 }
             }