diff --git a/src/client/videomanager.cpp b/src/client/videomanager.cpp
index 56ed87ea17d23eb267b3223748c5eb22c8ae0b0b..52410735952885d3c0d59c351dbab796eefcbdbb 100644
--- a/src/client/videomanager.cpp
+++ b/src/client/videomanager.cpp
@@ -49,6 +49,10 @@
 #include <cstring> // std::memset
 #include <ciso646> // fix windows compiler bug
 
+extern "C" {
+#include <libavutil/display.h>
+}
+
 namespace DRing {
 
 MediaFrame::MediaFrame()
@@ -341,6 +345,24 @@ VideoFrame::noise()
     }
 }
 
+int
+VideoFrame::getOrientation() const
+{
+    int32_t* matrix {nullptr};
+    if (auto p = packet()) {
+        matrix = reinterpret_cast<int32_t*>(av_packet_get_side_data(p, AV_PKT_DATA_DISPLAYMATRIX, nullptr));
+    } else if (auto p = pointer()) {
+        if (AVFrameSideData* side_data = av_frame_get_side_data(p, AV_FRAME_DATA_DISPLAYMATRIX)) {
+            matrix = reinterpret_cast<int32_t*>(side_data->data);
+        }
+    }
+    if (matrix) {
+        double angle = av_display_rotation_get(matrix);
+        return std::isnan(angle) ? 0 : -(int)angle;
+    }
+    return 0;
+}
+
 VideoFrame*
 getNewFrame()
 {
diff --git a/src/dring/videomanager_interface.h b/src/dring/videomanager_interface.h
index 04173a3a6cb965db48a51fb4b2ce62645c665e4c..7dbcea0e757e99535621fa0ff71ac0c01c228808 100644
--- a/src/dring/videomanager_interface.h
+++ b/src/dring/videomanager_interface.h
@@ -149,6 +149,9 @@ public:
     // Allocate internal pixel buffers following given specifications
     void reserve(int format, int width, int height);
 
+    // Return orientation (in degrees) stored in the frame metadata, or 0 by default.
+    int getOrientation() const;
+
     // Set internal pixel buffers on given memory buffer
     // This buffer must follow given specifications.
     void setFromMemory(uint8_t* data, int format, int width, int height) noexcept;
diff --git a/src/media/media_recorder.cpp b/src/media/media_recorder.cpp
index f2ee43918a6f4b45780b10e5a153509bac60f419..61b195c303a8cea8b1c79799c5f3712ed60abc2b 100644
--- a/src/media/media_recorder.cpp
+++ b/src/media/media_recorder.cpp
@@ -39,10 +39,6 @@
 #include <sys/types.h>
 #include <ctime>
 
-extern "C" {
-#include <libavutil/display.h>
-}
-
 namespace jami {
 
 const constexpr char ROTATION_FILTER_INPUT_NAME[] = "in";
@@ -87,11 +83,7 @@ struct MediaRecorder::StreamObserver : public Observer<std::shared_ptr<MediaFram
             else
 #endif
                 framePtr = std::static_pointer_cast<VideoFrame>(m);
-            AVFrameSideData* sideData = av_frame_get_side_data(framePtr->pointer(),
-                                                               AV_FRAME_DATA_DISPLAYMATRIX);
-            int angle = sideData
-                            ? -av_display_rotation_get(reinterpret_cast<int32_t*>(sideData->data))
-                            : 0;
+            int angle = framePtr->getOrientation();
             if (angle != rotation_) {
                 videoRotationFilter_ = jami::video::getTransposeFilter(angle,
                                                                        ROTATION_FILTER_INPUT_NAME,
diff --git a/src/media/video/sinkclient.cpp b/src/media/video/sinkclient.cpp
index 02f36f7dd9e95bf1c2455ca9abf8950e5bb825d5..e52b0ddd54dfeb56f687285d9bc5c523059b0814 100644
--- a/src/media/video/sinkclient.cpp
+++ b/src/media/video/sinkclient.cpp
@@ -58,10 +58,6 @@
 #include <stdexcept>
 #include <cmath>
 
-extern "C" {
-#include <libavutil/display.h>
-}
-
 namespace jami {
 namespace video {
 
@@ -367,14 +363,8 @@ SinkClient::update(Observable<std::shared_ptr<MediaFrame>>* /*obs*/,
                                                         AV_PIX_FMT_NV12);
         else
 #endif
-            frame = std::static_pointer_cast<VideoFrame>(frame_p);
-        AVFrameSideData* side_data = av_frame_get_side_data(frame->pointer(),
-                                                            AV_FRAME_DATA_DISPLAYMATRIX);
-        int angle = 0;
-        if (side_data) {
-            auto matrix_rotation = reinterpret_cast<int32_t*>(side_data->data);
-            angle = -av_display_rotation_get(matrix_rotation);
-        }
+        frame = std::static_pointer_cast<VideoFrame>(frame_p);
+        int angle = frame->getOrientation();
         if (angle != rotation_) {
             filter_ = getTransposeFilter(angle,
                                          FILTER_INPUT_NAME,
diff --git a/src/media/video/video_mixer.cpp b/src/media/video/video_mixer.cpp
index 172d1f5045e8f9362215438baa18a2ddf4462f4b..279a15ee8791c6e3dbafb31e6e518b4fa3af0420 100644
--- a/src/media/video/video_mixer.cpp
+++ b/src/media/video/video_mixer.cpp
@@ -38,10 +38,6 @@
 
 #include <opendht/thread_pool.h>
 
-extern "C" {
-#include <libavutil/display.h>
-}
-
 static constexpr auto MIN_LINE_ZOOM
     = 6; // Used by the ONE_BIG_WITH_SMALL layout for the small previews
 
@@ -307,13 +303,7 @@ VideoMixer::render_frame(VideoFrame& output,
     int xoff = source->x;
     int yoff = source->y;
 
-    AVFrameSideData* sideData = av_frame_get_side_data(frame->pointer(),
-                                                       AV_FRAME_DATA_DISPLAYMATRIX);
-    int angle = 0;
-    if (sideData) {
-        auto matrixRotation = reinterpret_cast<int32_t*>(sideData->data);
-        angle = -av_display_rotation_get(matrixRotation);
-    }
+    int angle = frame->getOrientation();
     const constexpr char filterIn[] = "mixin";
     if (angle != source->rotation) {
         source->rotationFilter = video::getTransposeFilter(angle,
diff --git a/src/media/video/video_sender.cpp b/src/media/video/video_sender.cpp
index 7a80811623594d3a89dee71fda159ecfe39d97e4..f643046bbd25145032f7ca78ed3772ccc8c7e799 100644
--- a/src/media/video/video_sender.cpp
+++ b/src/media/video/video_sender.cpp
@@ -36,9 +36,6 @@
 
 #include <map>
 #include <unistd.h>
-extern "C" {
-#include <libavutil/display.h>
-}
 
 namespace jami {
 namespace video {
@@ -84,6 +81,13 @@ VideoSender::~VideoSender()
 void
 VideoSender::encodeAndSendVideo(VideoFrame& input_frame)
 {
+    int angle = input_frame.getOrientation();
+    if (rotation_ != angle) {
+        rotation_ = angle;
+        if (changeOrientationCallback_)
+            changeOrientationCallback_(rotation_);
+    }
+
     if (auto packet = input_frame.packet()) {
 #if __ANDROID__
         if (forceKeyFrame_) {
@@ -91,17 +95,6 @@ VideoSender::encodeAndSendVideo(VideoFrame& input_frame)
             --forceKeyFrame_;
         }
 #endif
-        int size {0};
-        uint8_t* side_data = av_packet_get_side_data(packet, AV_PKT_DATA_DISPLAYMATRIX, &size);
-        auto angle = (side_data == nullptr || size == 0)
-                         ? 0
-                         : -av_display_rotation_get(reinterpret_cast<int32_t*>(side_data));
-        if (rotation_ != angle) {
-            rotation_ = angle;
-            if (changeOrientationCallback_)
-                changeOrientationCallback_(rotation_);
-        }
-
         videoEncoder_->send(*packet);
     } else {
         bool is_keyframe = forceKeyFrame_ > 0
@@ -110,17 +103,6 @@ VideoSender::encodeAndSendVideo(VideoFrame& input_frame)
         if (is_keyframe)
             --forceKeyFrame_;
 
-        AVFrameSideData* side_data = av_frame_get_side_data(input_frame.pointer(),
-                                                            AV_FRAME_DATA_DISPLAYMATRIX);
-        auto angle = side_data == nullptr
-                         ? 0
-                         : -av_display_rotation_get(reinterpret_cast<int32_t*>(side_data->data));
-        if (rotation_ != angle) {
-            rotation_ = angle;
-            if (changeOrientationCallback_)
-                changeOrientationCallback_(rotation_);
-        }
-
         if (videoEncoder_->encode(input_frame, is_keyframe, frameNumber_++) < 0)
             JAMI_ERR("encoding failed");
     }