From fbdc6a195759e2123512f6dc4f7e1e01bd37b3b8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Adrien=20B=C3=A9raud?= <adrien.beraud@savoirfairelinux.com>
Date: Mon, 9 May 2022 17:33:32 -0400
Subject: [PATCH] video receive thread: fix race condition on displayMatrix_

Instances of std::shared_ptr are not thread-safe, even if the control block is.

Since AVBufer already has a refcounting system, use a unique_ptr instead,
and add a mutex to protect access during copy.

In practice, the mutex should almost never block since it's locked for
the minimum scope.

Change-Id: I5122e76dadb5da6c3738f8dc711698744b52315b
---
 src/media/libav_utils.h                  |  9 +++++++++
 src/media/video/video_receive_thread.cpp | 22 ++++++++++++----------
 src/media/video/video_receive_thread.h   |  4 +++-
 3 files changed, 24 insertions(+), 11 deletions(-)

diff --git a/src/media/libav_utils.h b/src/media/libav_utils.h
index 942933c95c..6e91a785f8 100644
--- a/src/media/libav_utils.h
+++ b/src/media/libav_utils.h
@@ -24,11 +24,14 @@
 #include <vector>
 #include <map>
 #include <string>
+#include <memory>
 
 extern "C" {
 struct AVDictionary;
 struct AVFrame;
 struct AVPixFmtDescriptor;
+struct AVBufferRef;
+void av_buffer_unref(AVBufferRef **buf);
 }
 
 namespace jami {
@@ -51,5 +54,11 @@ void fillWithBlack(AVFrame* frame);
 
 void fillWithSilence(AVFrame* frame);
 
+struct AVBufferRef_deleter {
+    void operator()(AVBufferRef* buf) const { av_buffer_unref(&buf); }
+};
+
+typedef std::unique_ptr<AVBufferRef, AVBufferRef_deleter> AVBufferPtr;
+
 } // namespace libav_utils
 } // namespace jami
diff --git a/src/media/video/video_receive_thread.cpp b/src/media/video/video_receive_thread.cpp
index e86c35f86c..42274a8f9e 100644
--- a/src/media/video/video_receive_thread.cpp
+++ b/src/media/video/video_receive_thread.cpp
@@ -92,10 +92,16 @@ VideoReceiveThread::setup()
     JAMI_DBG("[%p] Setupping video receiver", this);
 
     videoDecoder_.reset(new MediaDecoder([this](const std::shared_ptr<MediaFrame>& frame) mutable {
-        if (auto displayMatrix = displayMatrix_)
+        libav_utils::AVBufferPtr displayMatrix;
+        {
+            std::lock_guard<std::mutex> l(rotationMtx_);
+            if (displayMatrix_)
+                displayMatrix.reset(av_buffer_ref(displayMatrix_.get()));
+        }
+        if (displayMatrix)
             av_frame_new_side_data_from_buf(frame->pointer(),
                                             AV_FRAME_DATA_DISPLAYMATRIX,
-                                            av_buffer_ref(displayMatrix.get()));
+                                            displayMatrix.release());
         publishFrame(std::static_pointer_cast<VideoFrame>(frame));
     }));
     videoDecoder_->setResolutionChangedCallback([this](int width, int height) {
@@ -303,14 +309,10 @@ VideoReceiveThread::getInfo() const
 void
 VideoReceiveThread::setRotation(int angle)
 {
-    std::shared_ptr<AVBufferRef> displayMatrix {av_buffer_alloc(sizeof(int32_t) * 9),
-                                                [](AVBufferRef* buf) {
-                                                    av_buffer_unref(&buf);
-                                                }};
-    if (displayMatrix) {
-        av_display_rotation_set(reinterpret_cast<int32_t*>(displayMatrix->data), angle);
-        displayMatrix_ = std::move(displayMatrix);
-    }
+    libav_utils::AVBufferPtr displayMatrix(av_buffer_alloc(sizeof(int32_t) * 9));
+    av_display_rotation_set(reinterpret_cast<int32_t*>(displayMatrix->data), angle);
+    std::lock_guard<std::mutex> l(rotationMtx_);
+    displayMatrix_ = std::move(displayMatrix);
 }
 
 } // namespace video
diff --git a/src/media/video/video_receive_thread.h b/src/media/video/video_receive_thread.h
index c4b156c683..b1b1998a1f 100644
--- a/src/media/video/video_receive_thread.h
+++ b/src/media/video/video_receive_thread.h
@@ -31,6 +31,7 @@
 #include "media_stream.h"
 #include "threadloop.h"
 #include "noncopyable.h"
+#include "libav_utils.h"
 
 #include <functional>
 #include <map>
@@ -106,7 +107,8 @@ private:
     uint16_t mtu_;
     int rotation_ {0};
 
-    std::shared_ptr<AVBufferRef> displayMatrix_;
+    std::mutex rotationMtx_;
+    libav_utils::AVBufferPtr displayMatrix_;
 
     static int interruptCb(void* ctx);
     static int readFunction(void* opaque, uint8_t* buf, int buf_size);
-- 
GitLab