diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index e61bb5736990cef2e402d0b7559a7504cd26581f..e5957f90bd4ec2e07bdc1027c2f0f7502291a396 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -51,7 +51,6 @@ list (APPEND Source_Files
       "${CMAKE_CURRENT_SOURCE_DIR}/registration_states.h"
       "${CMAKE_CURRENT_SOURCE_DIR}/ring_api.cpp"
       "${CMAKE_CURRENT_SOURCE_DIR}/ring_types.h"
-      "${CMAKE_CURRENT_SOURCE_DIR}/rw_mutex.h"
       "${CMAKE_CURRENT_SOURCE_DIR}/scheduled_executor.cpp"
       "${CMAKE_CURRENT_SOURCE_DIR}/scheduled_executor.h"
       "${CMAKE_CURRENT_SOURCE_DIR}/smartools.cpp"
@@ -113,4 +112,4 @@ set (Source_Files__upnp ${Source_Files__upnp} PARENT_SCOPE)
 set (Source_Files__upnp__protocol ${Source_Files__upnp__protocol} PARENT_SCOPE)
 set (Source_Files__upnp__protocol__pupnp ${Source_Files__upnp__protocol__pupnp} PARENT_SCOPE)
 set (Source_Files__upnp__protocol__natpmp ${Source_Files__upnp__protocol__natpmp} PARENT_SCOPE)
-set (Source_Files__plugin ${Source_Files__plugin} PARENT_SCOPE)
\ No newline at end of file
+set (Source_Files__plugin ${Source_Files__plugin} PARENT_SCOPE)
diff --git a/src/Makefile.am b/src/Makefile.am
index 14bdfaf0eacb94057277d33af48f9766a6d4c17a..b83726a94ffb1bf79dcef6bfe033a7aa11332b8c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -112,7 +112,6 @@ libring_la_SOURCES = \
 		map_utils.h \
 		string_utils.h \
 		string_utils.cpp \
-		rw_mutex.h \
 		ring_api.cpp \
 		rational.h \
 		smartools.cpp \
diff --git a/src/media/video/video_mixer.cpp b/src/media/video/video_mixer.cpp
index e6506d7ceb8aaf487dffefb50879cbbba1f46d70..f45971c896ebbecafc454350f334ed3917fce711 100644
--- a/src/media/video/video_mixer.cpp
+++ b/src/media/video/video_mixer.cpp
@@ -35,6 +35,7 @@
 
 #include <cmath>
 #include <unistd.h>
+#include <mutex>
 
 #include <opendht/thread_pool.h>
 
@@ -209,7 +210,7 @@ VideoMixer::updateLayout()
 void
 VideoMixer::attached(Observable<std::shared_ptr<MediaFrame>>* ob)
 {
-    auto lock(rwMutex_.write());
+    std::unique_lock lock(rwMutex_);
 
     auto src = std::unique_ptr<VideoMixerSource>(new VideoMixerSource);
     src->render_frame = std::make_shared<VideoFrame>();
@@ -223,7 +224,7 @@ VideoMixer::attached(Observable<std::shared_ptr<MediaFrame>>* ob)
 void
 VideoMixer::detached(Observable<std::shared_ptr<MediaFrame>>* ob)
 {
-    auto lock(rwMutex_.write());
+    std::unique_lock lock(rwMutex_);
 
     for (const auto& x : sources_) {
         if (x->source == ob) {
@@ -244,7 +245,7 @@ void
 VideoMixer::update(Observable<std::shared_ptr<MediaFrame>>* ob,
                    const std::shared_ptr<MediaFrame>& frame_p)
 {
-    auto lock(rwMutex_.read());
+    std::shared_lock lock(rwMutex_);
 
     for (const auto& x : sources_) {
         if (x->source == ob) {
@@ -292,7 +293,7 @@ VideoMixer::process()
 
     {
         std::lock_guard<std::mutex> lk(audioOnlySourcesMtx_);
-        auto lock(rwMutex_.read());
+        std::shared_lock lock(rwMutex_);
 
         int i = 0;
         bool activeFound = false;
@@ -507,7 +508,7 @@ VideoMixer::calc_position(std::unique_ptr<VideoMixerSource>& source,
 void
 VideoMixer::setParameters(int width, int height, AVPixelFormat format)
 {
-    auto lock(rwMutex_.write());
+    std::unique_lock lock(rwMutex_);
 
     width_ = width;
     height_ = height;
diff --git a/src/media/video/video_mixer.h b/src/media/video/video_mixer.h
index 4f391b7e76274e6f98370427cf9e949c79b3c3bf..d271c9fd492c39a0c5636d045447c046d5a483d7 100644
--- a/src/media/video/video_mixer.h
+++ b/src/media/video/video_mixer.h
@@ -25,12 +25,12 @@
 #include "video_base.h"
 #include "video_scaler.h"
 #include "threadloop.h"
-#include "rw_mutex.h"
 #include "media_stream.h"
 
 #include <list>
 #include <chrono>
 #include <memory>
+#include <shared_mutex>
 
 namespace jami {
 namespace video {
@@ -143,7 +143,7 @@ private:
     int width_ = 0;
     int height_ = 0;
     AVPixelFormat format_ = AV_PIX_FMT_YUV422P;
-    rw_mutex rwMutex_;
+    std::shared_mutex rwMutex_;
 
     std::shared_ptr<SinkClient> sink_;
 
diff --git a/src/rw_mutex.h b/src/rw_mutex.h
deleted file mode 100644
index 5eb13daffd91984cbf5c78bd531c0bd9e309451b..0000000000000000000000000000000000000000
--- a/src/rw_mutex.h
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- *  Copyright (C) 2004-2022 Savoir-faire Linux Inc.
- *
- *  Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 3 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
- */
-
-#ifndef RW_MUTEX_H_
-#define RW_MUTEX_H_
-
-#include "noncopyable.h"
-
-#include <mutex>
-#include <atomic>
-#include <condition_variable>
-#include <string>
-#include <sstream>
-
-namespace jami {
-
-/**
- * rw_mutex is a shared mutex meant to protect
- * rarely-modified, often-read data structures.
- *
- * Its goal is to optimize read throughput and latency.
- * Multiple threads can concurrently read data while
- * a writer thread gets exclusive access when needed.
- */
-class rw_mutex
-{
-public:
-    rw_mutex()
-        : mutex()
-        , canRead()
-        , canWrite()
-        , readers(0)
-        , writing(false)
-    {}
-    void read_enter()
-    {
-        std::unique_lock<std::mutex> lck(mutex);
-        canRead.wait(lck, [this]() { return !writing; });
-        readers++;
-    }
-    void read_exit()
-    {
-        // std::lock_guard<std::mutex> lck(mutex);
-        readers--;
-        canWrite.notify_one();
-    }
-    void write_enter()
-    {
-        std::unique_lock<std::mutex> lck(mutex);
-        canWrite.wait(lck, [this]() { return !writing && readers == 0; });
-        writing = true;
-    }
-    void write_exit()
-    {
-        std::lock_guard<std::mutex> lck(mutex);
-        writing = false;
-        canWrite.notify_one();
-        canRead.notify_all();
-    }
-
-    struct read_lock
-    {
-    public:
-        read_lock(rw_mutex& m)
-            : sem(m)
-        {
-            sem.read_enter();
-        }
-        ~read_lock() { sem.read_exit(); }
-
-    private:
-        rw_mutex& sem;
-    };
-
-    struct write_lock
-    {
-    public:
-        write_lock(rw_mutex& m)
-            : sem(m)
-        {
-            sem.write_enter();
-        }
-        ~write_lock() { sem.write_exit(); }
-
-    private:
-        rw_mutex& sem;
-    };
-
-    read_lock read() { return read_lock(*this); }
-
-    write_lock write() { return write_lock(*this); }
-
-    std::string toString()
-    {
-        std::stringstream ss;
-        ss << "[rw_mutex write:" << (writing ? "LOCKED" : "unlocked") << " read:" << readers << "]";
-        return ss.str();
-    }
-
-private:
-    NON_COPYABLE(rw_mutex);
-    std::mutex mutex;
-    std::condition_variable canRead, canWrite;
-    std::atomic<unsigned> readers;
-    bool writing;
-};
-
-} // namespace jami
-
-#endif