diff --git a/pixmaps/pixmaps.gresource.xml b/pixmaps/pixmaps.gresource.xml
index f641e566d68677e5fc135ada0125700a557b812f..bdfbbf9615fb4832435df01a8efb47caa15cdce8 100644
--- a/pixmaps/pixmaps.gresource.xml
+++ b/pixmaps/pixmaps.gresource.xml
@@ -23,7 +23,9 @@
     <file alias="mute_video">ic_videocam_white_24px.svg</file>
     <file alias="pause">ic_pause_white_24px.svg</file>
     <file alias="stop">baseline-stop-24px.svg</file>
+    <file alias="stop-white">stop-white.svg</file>
     <file alias="send">baseline-send-24px.svg</file>
+    <file alias="send-white">send-white.svg</file>
     <file alias="play">ic_play_arrow_white_24px.svg</file>
     <file alias="quality">ic_high_quality_white_24px.svg</file>
     <file alias="contacts_list">ic_people_black_24px.svg</file>
@@ -52,5 +54,6 @@
     <file alias="bottom_arrow">bottom_arrow.svg</file>
     <file alias="up_arrow">up_arrow.svg</file>
     <file alias="qrcode">qrcode.svg</file>
+    <file alias="retry">retry.svg</file>
   </gresource>
 </gresources>
diff --git a/pixmaps/retry.svg b/pixmaps/retry.svg
new file mode 100644
index 0000000000000000000000000000000000000000..d6f605cd7ed5c15be646a91ea3568a6f01ee9db0
--- /dev/null
+++ b/pixmaps/retry.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 5V1L7 6l5 5V7c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6H4c0 4.42 3.58 8 8 8s8-3.58 8-8-3.58-8-8-8z" fill="#ffffff"/></svg>
\ No newline at end of file
diff --git a/pixmaps/send-white.svg b/pixmaps/send-white.svg
new file mode 100644
index 0000000000000000000000000000000000000000..f59133421c5fea32e77dee39d3b814e324fee265
--- /dev/null
+++ b/pixmaps/send-white.svg
@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z" fill="white"/>
+    <path d="M0 0h24v24H0z" fill="none"/>
+</svg>
diff --git a/pixmaps/stop-white.svg b/pixmaps/stop-white.svg
new file mode 100644
index 0000000000000000000000000000000000000000..63d1d1c8a3e1dd10874f9bf74e611dea30389ff8
--- /dev/null
+++ b/pixmaps/stop-white.svg
@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24">
+    <path d="M0 0h24v24H0z" fill="white"/>
+    <path d="M6 6h12v12H6z" fill="none"/>
+</svg>
diff --git a/src/chatview.cpp b/src/chatview.cpp
index 63842501a8d7b901b87f36f785a3eb3282b9806f..3200d62298dc7215845d6a558f7665dbb0b8f189 100644
--- a/src/chatview.cpp
+++ b/src/chatview.cpp
@@ -29,25 +29,36 @@
 
 // GTK
 #include <glib/gi18n.h>
+#include <clutter-gtk/clutter-gtk.h>
 
 // Qt
 #include <QSize>
 
 // LRC
+#include <api/call.h>
 #include <api/contactmodel.h>
 #include <api/conversationmodel.h>
 #include <api/contact.h>
+#include <api/lrc.h>
 #include <api/newcallmodel.h>
-#include <api/call.h>
+#include <api/avmodel.h>
 
 // Client
 #include "marshals.h"
 #include "utils/files.h"
 #include "native/pixbufmanipulator.h"
+#include "video/video_widget.h"
+
 /* size of avatar */
 static constexpr int AVATAR_WIDTH  = 150; /* px */
 static constexpr int AVATAR_HEIGHT = 150; /* px */
 
+enum class RecordAction {
+    RECORD,
+    STOP,
+    SEND
+};
+
 struct CppImpl {
     struct Interaction {
         std::string conv;
@@ -55,6 +66,11 @@ struct CppImpl {
         lrc::api::interaction::Info info;
     };
     std::vector<Interaction> interactionsBuffer_;
+    lrc::api::AVModel* avModel_;
+    RecordAction current_action_ {RecordAction::RECORD};
+
+    // store current recording location
+    std::string saveFileName_;
 };
 
 struct _ChatView
@@ -83,13 +99,25 @@ struct _ChatViewPrivate
     QMetaObject::Connection interaction_removed;
     QMetaObject::Connection update_interaction_connection;
     QMetaObject::Connection update_add_to_conversations;
+    QMetaObject::Connection local_renderer_connection;
 
     gulong webkit_ready;
     gulong webkit_send_text;
     gulong webkit_drag_drop;
 
     bool ready_ {false};
-    CppImpl* cpp_;
+    bool readyToRecord_ {false};
+    CppImpl* cpp;
+
+    bool video_started_by_settings;
+    GtkWidget* video_widget;
+    GtkWidget* record_popover;
+    GtkWidget* button_retry;
+    GtkWidget* button_main_action;
+    GtkWidget* label_time;
+    guint timer_duration = 0;
+    uint32_t duration = 0;
+
 };
 
 G_DEFINE_TYPE_WITH_PRIVATE(ChatView, chat_view, GTK_TYPE_BOX);
@@ -108,6 +136,9 @@ enum {
 
 static guint chat_view_signals[LAST_SIGNAL] = { 0 };
 
+static void
+init_video_widget(ChatView* self);
+
 static void
 chat_view_dispose(GObject *object)
 {
@@ -121,6 +152,7 @@ chat_view_dispose(GObject *object)
     QObject::disconnect(priv->update_interaction_connection);
     QObject::disconnect(priv->interaction_removed);
     QObject::disconnect(priv->update_add_to_conversations);
+    QObject::disconnect(priv->local_renderer_connection);
 
     /* Destroying the box will also destroy its children, and we wouldn't
      * want that. So we remove the webkit_chat_container from the box. */
@@ -140,6 +172,14 @@ chat_view_dispose(GObject *object)
         priv->webkit_chat_container = nullptr;
     }
 
+    if (priv->video_widget) {
+        gtk_widget_destroy(priv->video_widget);
+    }
+
+    if (priv->record_popover) {
+        gtk_widget_destroy(priv->record_popover);
+    }
+
     G_OBJECT_CLASS(chat_view_parent_class)->dispose(object);
 }
 
@@ -223,6 +263,128 @@ file_to_manipulate(GtkWindow* top_window, bool send)
 
 static void update_chatview_frame(ChatView *self);
 
+void
+on_record_closed(GtkPopover*, ChatView *self)
+{
+    g_return_if_fail(IS_CHAT_VIEW(self));
+    auto* priv = CHAT_VIEW_GET_PRIVATE(self);
+
+    if (!priv->cpp->saveFileName_.empty()) {
+        priv->cpp->avModel_->stopLocalRecorder(priv->cpp->saveFileName_);
+        priv->cpp->saveFileName_ = "";
+    }
+
+    priv->cpp->current_action_ = RecordAction::RECORD;
+    if (priv->timer_duration) g_source_remove(priv->timer_duration);
+    priv->cpp->avModel_->stopPreview();
+    priv->duration = 0;
+}
+
+void
+chat_view_show_video_recorder(ChatView *self, int pt_x, int pt_y)
+{
+    g_return_if_fail(IS_CHAT_VIEW(self));
+    auto* priv = CHAT_VIEW_GET_PRIVATE(self);
+    if (!priv->readyToRecord_) return;
+
+    priv->cpp->avModel_->startPreview();
+
+    // CSS styles
+    auto provider = gtk_css_provider_new();
+    gtk_css_provider_load_from_data(provider,
+        ".record-button { background: rgba(0, 0, 0, 0.2); border-radius: 50%; border: 0; transition: all 0.3s ease; } \
+        .record-button:hover { background: rgba(0, 0, 0, 0.2); border-radius: 50%; border: 0; transition: all 0.3s ease; } \
+        .label_time { color: white; }",
+        -1, nullptr
+    );
+    gtk_style_context_add_provider_for_screen(gdk_display_get_default_screen(gdk_display_get_default()),
+                                              GTK_STYLE_PROVIDER(provider),
+                                              GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+
+    auto deviceName = priv->cpp->avModel_->getDefaultDeviceName();
+    auto settings = priv->cpp->avModel_->getDeviceSettings(deviceName);
+    auto res = settings.size;
+    if (res.find("x") == std::string::npos) return;
+    auto width = static_cast<double>(std::stoi(res.substr(0, res.find("x"))));
+    auto height = static_cast<double>(std::stoi(res.substr(res.find("x") + 1)));
+    auto max = std::max(width, height);
+#if GTK_CHECK_VERSION(3,22,0)
+    GdkRectangle workarea = {};
+    gdk_monitor_get_workarea(
+        gdk_display_get_primary_monitor(gdk_display_get_default()),
+        &workarea);
+    auto widget_size = std::max(300, workarea.width / 6);
+#else
+    auto widget_size = std::max(300, gdk_screen_width() / 6);
+#endif
+
+    width = width / max * widget_size;
+    height = height / max * widget_size;
+
+    if (priv->record_popover) {
+        gtk_widget_destroy(priv->record_popover);
+    }
+    init_video_widget(self);
+
+    priv->record_popover = gtk_popover_new(GTK_WIDGET(priv->box_webkit_chat_container));
+    g_signal_connect(priv->record_popover, "closed", G_CALLBACK(on_record_closed), self);
+    gtk_popover_set_relative_to(GTK_POPOVER(priv->record_popover), GTK_WIDGET(priv->box_webkit_chat_container));
+    gtk_container_add(GTK_CONTAINER(GTK_POPOVER(priv->record_popover)), priv->video_widget);
+    GdkRectangle rect;
+    rect.width = 1;
+    rect.height = 1;
+    rect.x = pt_x;
+    rect.y = pt_y;
+    gtk_popover_set_pointing_to(GTK_POPOVER(priv->record_popover), &rect);
+    gtk_widget_set_size_request(GTK_WIDGET(priv->video_widget), width, height);
+#if GTK_CHECK_VERSION(3,22,0)
+    gtk_popover_popdown(GTK_POPOVER(priv->record_popover));
+#endif
+    gtk_widget_show_all(priv->record_popover);
+}
+
+static gboolean
+on_timer_duration_timeout(ChatView* view)
+{
+    g_return_val_if_fail(IS_CHAT_VIEW(view), G_SOURCE_REMOVE);
+    auto* priv = CHAT_VIEW_GET_PRIVATE(view);
+    priv->duration += 1;
+    auto m = std::to_string(priv->duration / 60);
+    if (m.length() == 1) {
+        m = "0" + m;
+    }
+    auto s = std::to_string(priv->duration % 60);
+    if (s.length() == 1) {
+        s = "0" + s;
+    }
+    auto time_txt = m + ":" + s;
+    gtk_label_set_text(GTK_LABEL(priv->label_time), time_txt.c_str());
+    return G_SOURCE_CONTINUE;
+}
+
+static void
+reset_recorder(ChatView *self)
+{
+    g_return_if_fail(IS_CHAT_VIEW(self));
+    auto* priv = CHAT_VIEW_GET_PRIVATE(self);
+
+    gtk_widget_hide(GTK_WIDGET(priv->button_retry));
+    auto image = gtk_image_new_from_resource ("/net/jami/JamiGnome/stop-white");
+    gtk_button_set_image(GTK_BUTTON(priv->button_main_action), image);
+    priv->cpp->current_action_ = RecordAction::STOP;
+    gtk_label_set_text(GTK_LABEL(priv->label_time), "00:00");
+    priv->duration = 0;
+    priv->timer_duration = g_timeout_add(1000, (GSourceFunc)on_timer_duration_timeout, self);
+
+    std::string file_name = priv->cpp->avModel_->startLocalRecorder(false);
+    if (file_name.empty()) {
+        g_warning("set_state: failed to start recording");
+        return;
+    }
+
+    priv->cpp->saveFileName_ = file_name;
+}
+
 static void
 webkit_chat_container_script_dialog(GtkWidget* webview, gchar *interaction, ChatView* self)
 {
@@ -334,6 +496,18 @@ webkit_chat_container_script_dialog(GtkWidget* webview, gchar *interaction, Chat
         } catch (...) {
             g_warning("delete interaction failed: can't find %s", order.substr(std::string("RETRY_INTERACTION:").size()).c_str());
         }
+    } else if (order.find("VIDEO_RECORD:") == 0) {
+        auto pos_str {order.substr(std::string("VIDEO_RECORD:").size())};
+        auto sep_idx = pos_str.find("x");
+        if (sep_idx == std::string::npos)
+            return;
+        try {
+            int pt_x = stoi(pos_str.substr(0, sep_idx));
+            int pt_y = stoi(pos_str.substr(sep_idx + 1));
+            chat_view_show_video_recorder(self, pt_x, pt_y);
+        } catch (...) {
+            // ignore
+        }
     }
 }
 
@@ -541,7 +715,7 @@ webkit_chat_container_ready(ChatView* self)
     load_participants_images(self);
 
     priv->ready_ = true;
-    for (const auto& interaction: priv->cpp_->interactionsBuffer_) {
+    for (const auto& interaction: priv->cpp->interactionsBuffer_) {
         if (interaction.conv == priv->conversation_->uid) {
             print_interaction_to_buffer(self, interaction.id, interaction.info);
         }
@@ -642,6 +816,7 @@ update_chatview_frame(ChatView* self)
             }
         }
     } catch (const std::out_of_range&) {}
+    chat_view_set_record_visible(self, lrc::api::Lrc::activeCalls().size() == 0);
 }
 
 static void
@@ -678,6 +853,118 @@ on_webkit_drag_drop(GtkWidget*, gchar* data, ChatView* self)
     }
 }
 
+static void
+on_main_action_clicked(ChatView *self)
+{
+    g_return_if_fail(IS_CHAT_VIEW(self));
+    auto* priv = CHAT_VIEW_GET_PRIVATE(self);
+
+    switch (priv->cpp->current_action_) {
+        case RecordAction::RECORD: {
+            reset_recorder(self);
+            break;
+        }
+        case RecordAction::STOP: {
+            if (!priv->cpp->saveFileName_.empty()) {
+                priv->cpp->avModel_->stopLocalRecorder(priv->cpp->saveFileName_);
+            }
+            gtk_widget_show(GTK_WIDGET(priv->button_retry));
+            auto image = gtk_image_new_from_resource ("/net/jami/JamiGnome/send-white");
+            gtk_button_set_image(GTK_BUTTON(priv->button_main_action), image);
+            priv->cpp->current_action_ = RecordAction::SEND;
+            g_source_remove(priv->timer_duration);
+            break;
+        }
+        case RecordAction::SEND: {
+            if (auto model = (*priv->accountInfo_)->conversationModel.get()) {
+                model->sendFile(priv->conversation_->uid, priv->cpp->saveFileName_, g_path_get_basename(priv->cpp->saveFileName_.c_str()));
+                priv->cpp->saveFileName_ = "";
+            }
+            gtk_widget_destroy(priv->record_popover);
+            priv->cpp->current_action_ = RecordAction::RECORD;
+            priv->cpp->avModel_->stopPreview();
+            break;
+        }
+    }
+}
+
+static void
+init_video_widget(ChatView* self)
+{
+    ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
+    if (priv->video_widget && GTK_IS_WIDGET(priv->video_widget)) gtk_widget_destroy(priv->video_widget);
+    priv->video_widget = video_widget_new();
+
+    try {
+        const lrc::api::video::Renderer* previewRenderer =
+            &priv->cpp->avModel_->getRenderer(
+            lrc::api::video::PREVIEW_RENDERER_ID);
+        priv->video_started_by_settings = previewRenderer->isRendering();
+        if (priv->video_started_by_settings) {
+            video_widget_add_new_renderer(VIDEO_WIDGET(priv->video_widget),
+                priv->cpp->avModel_, previewRenderer, VIDEO_RENDERER_REMOTE);
+        } else {
+            priv->video_started_by_settings = true;
+            priv->local_renderer_connection = QObject::connect(
+                &*priv->cpp->avModel_,
+                &lrc::api::AVModel::rendererStarted,
+                [=](const std::string& id) {
+                    if (id != lrc::api::video::PREVIEW_RENDERER_ID
+                        || !priv->readyToRecord_)
+                        return;
+                    video_widget_add_new_renderer(
+                        VIDEO_WIDGET(priv->video_widget),
+                        priv->cpp->avModel_,
+                        previewRenderer, VIDEO_RENDERER_REMOTE);
+                });
+        }
+    } catch (const std::out_of_range& e) {
+        g_warning("Cannot start preview");
+    }
+
+    auto stage = gtk_clutter_embed_get_stage(GTK_CLUTTER_EMBED(priv->video_widget));
+
+    auto* hbox_record_controls = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 15);
+
+    auto image_retry = gtk_image_new_from_resource("/net/jami/JamiGnome/retry");
+    auto image_record = gtk_image_new_from_resource("/net/jami/JamiGnome/record");
+
+    priv->button_retry = gtk_button_new();
+    gtk_button_set_relief(GTK_BUTTON(priv->button_retry), GTK_RELIEF_NONE);
+    gtk_widget_set_tooltip_text(priv->button_retry, _("Retry"));
+    gtk_button_set_image(GTK_BUTTON(priv->button_retry), image_retry);
+    gtk_widget_set_size_request(GTK_WIDGET(priv->button_retry), 48, 48);
+    GtkStyleContext* context;
+    context = gtk_widget_get_style_context(GTK_WIDGET(priv->button_retry));
+    gtk_style_context_add_class(context, "record-button");
+    g_signal_connect_swapped(priv->button_retry, "clicked", G_CALLBACK(reset_recorder), self);
+
+    priv->button_main_action = gtk_button_new();
+    gtk_button_set_relief(GTK_BUTTON(priv->button_main_action), GTK_RELIEF_NONE);
+    gtk_widget_set_tooltip_text(priv->button_main_action, _("Record"));
+    gtk_button_set_image(GTK_BUTTON(priv->button_main_action), image_record);
+    gtk_widget_set_size_request(GTK_WIDGET(priv->button_main_action), 48, 48);
+    context = gtk_widget_get_style_context(GTK_WIDGET(priv->button_main_action));
+    gtk_style_context_add_class(context, "record-button");
+    g_signal_connect_swapped(priv->button_main_action, "clicked", G_CALLBACK(on_main_action_clicked), self);
+
+    priv->label_time = gtk_label_new("00:00");
+    context = gtk_widget_get_style_context(GTK_WIDGET(priv->label_time));
+    gtk_style_context_add_class(context, "label_time");
+
+    gtk_container_add(GTK_CONTAINER(hbox_record_controls), priv->button_retry);
+    gtk_container_add(GTK_CONTAINER(hbox_record_controls), priv->button_main_action);
+    gtk_container_add(GTK_CONTAINER(hbox_record_controls), priv->label_time);
+
+    gtk_widget_show_all(hbox_record_controls);
+    gtk_widget_hide(priv->button_retry);
+
+    auto actor_controls = gtk_clutter_actor_new_with_contents(hbox_record_controls);
+    clutter_actor_add_child(stage, actor_controls);
+    clutter_actor_set_x_align(actor_controls, CLUTTER_ACTOR_ALIGN_CENTER);
+    clutter_actor_set_y_align(actor_controls, CLUTTER_ACTOR_ALIGN_END);
+}
+
 static void
 build_chat_view(ChatView* self)
 {
@@ -706,20 +993,19 @@ build_chat_view(ChatView* self)
         G_CALLBACK(on_webkit_drag_drop),
         self
     );
-
     priv->new_interaction_connection = QObject::connect(
     &*(*priv->accountInfo_)->conversationModel, &lrc::api::ConversationModel::newInteraction,
     [self, priv](const std::string& uid, uint64_t interactionId, lrc::api::interaction::Info interaction) {
         if (!priv->conversation_) return;
-        if (!priv->ready_ && priv->cpp_) {
-            priv->cpp_->interactionsBuffer_.emplace_back(CppImpl::Interaction {
+        if (!priv->ready_ && priv->cpp) {
+            priv->cpp->interactionsBuffer_.emplace_back(CppImpl::Interaction {
                 uid, interactionId, interaction});
         } else if (uid == priv->conversation_->uid) {
             print_interaction_to_buffer(self, interactionId, interaction);
         }
     });
 
-    priv->cpp_ = new CppImpl();
+    priv->cpp = new CppImpl();
 
     if (webkit_chat_container_is_ready(WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container)))
         webkit_chat_container_ready(self);
@@ -728,7 +1014,8 @@ build_chat_view(ChatView* self)
 GtkWidget *
 chat_view_new (WebKitChatContainer* webkit_chat_container,
                AccountInfoPointer const & accountInfo,
-               lrc::api::conversation::Info* conversation)
+               lrc::api::conversation::Info* conversation,
+               lrc::api::AVModel& avModel)
 {
     ChatView *self = CHAT_VIEW(g_object_new(CHAT_VIEW_TYPE, NULL));
 
@@ -738,6 +1025,8 @@ chat_view_new (WebKitChatContainer* webkit_chat_container,
     priv->accountInfo_ = &accountInfo;
 
     build_chat_view(self);
+    priv->cpp->avModel_ = &avModel;
+    priv->readyToRecord_ = true;
     return (GtkWidget *)self;
 }
 
@@ -762,3 +1051,10 @@ chat_view_set_header_visible(ChatView *self, gboolean visible)
     auto priv = CHAT_VIEW_GET_PRIVATE(self);
     webkit_chat_set_header_visible(WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container), visible);
 }
+
+void
+chat_view_set_record_visible(ChatView *self, gboolean visible)
+{
+    auto priv = CHAT_VIEW_GET_PRIVATE(self);
+    webkit_chat_set_record_visible(WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container), visible);
+}
diff --git a/src/chatview.h b/src/chatview.h
index f052f9dd13a9ac911e8de6984a2a2516a1e6155c..f0fd444b516c602bd49f9c9afebf02c450319f6b 100644
--- a/src/chatview.h
+++ b/src/chatview.h
@@ -34,6 +34,7 @@ namespace lrc
 {
 namespace api
 {
+class AVModel;
 namespace conversation
 {
     struct Info;
@@ -55,9 +56,11 @@ typedef struct _ChatViewClass ChatViewClass;
 GType          chat_view_get_type   (void) G_GNUC_CONST;
 GtkWidget     *chat_view_new        (WebKitChatContainer* view,
                                      AccountInfoPointer const & accountInfo,
-                                     lrc::api::conversation::Info* conversation);
+                                     lrc::api::conversation::Info* conversation,
+                                     lrc::api::AVModel& avModel);
 lrc::api::conversation::Info chat_view_get_conversation(ChatView*);
 void chat_view_update_temporary(ChatView*);
 void chat_view_set_header_visible(ChatView*, gboolean);
+void chat_view_set_record_visible(ChatView*, gboolean);
 
 G_END_DECLS
diff --git a/src/currentcallview.cpp b/src/currentcallview.cpp
index 97cbba20e55d0f9598a630cc08e6e3a38126dad8..f8b697ac87181e71833a9dcfe30ed7c6125367ec 100644
--- a/src/currentcallview.cpp
+++ b/src/currentcallview.cpp
@@ -1298,10 +1298,11 @@ CppImpl::setCallInfo()
 
     // init chat view
     widgets->chat_view = chat_view_new(WEBKIT_CHAT_CONTAINER(widgets->webkit_chat_container),
-                                       *accountInfo, conversation);
+                                       *accountInfo, conversation, *avModel_);
     gtk_container_add(GTK_CONTAINER(widgets->frame_chat), widgets->chat_view);
 
     chat_view_set_header_visible(CHAT_VIEW(widgets->chat_view), FALSE);
+    chat_view_set_record_visible(CHAT_VIEW(widgets->chat_view), FALSE);
 }
 
 void
diff --git a/src/incomingcallview.cpp b/src/incomingcallview.cpp
index cfbeab3ae353dde88680d800b8e8ff3b23f4b000..be92d64322fdc4f3057c3789fe8d750dbf43889b 100644
--- a/src/incomingcallview.cpp
+++ b/src/incomingcallview.cpp
@@ -83,6 +83,8 @@ struct _IncomingCallViewPrivate
     QMetaObject::Connection state_change_connection;
 
     GSettings *settings;
+
+    lrc::api::AVModel* avModel_;
 };
 
 G_DEFINE_TYPE_WITH_PRIVATE(IncomingCallView, incoming_call_view, GTK_TYPE_BOX);
@@ -295,9 +297,11 @@ set_call_info(IncomingCallView *view) {
 
     auto chat_view = chat_view_new(WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
                                                          *priv->accountInfo_,
-                                                         priv->conversation_);
+                                                         priv->conversation_,
+                                                         *priv->avModel_);
     gtk_widget_show(chat_view);
     chat_view_set_header_visible(CHAT_VIEW(chat_view), FALSE);
+    chat_view_set_record_visible(CHAT_VIEW(chat_view), FALSE);
     gtk_container_add(GTK_CONTAINER(priv->frame_chat), chat_view);
 }
 
@@ -313,6 +317,7 @@ incoming_call_view_new(WebKitChatContainer* view,
     priv->webkit_chat_container = GTK_WIDGET(view);
     priv->conversation_ = conversation;
     priv->accountInfo_ = &accountInfo;
+    priv->avModel_ = &avModel;
 
     priv->messaging_widget = messaging_widget_new(avModel, conversation, accountInfo);
     gtk_box_pack_start(GTK_BOX(priv->box_messaging_widget), priv->messaging_widget, TRUE, TRUE, 0);
diff --git a/src/mediasettingsview.cpp b/src/mediasettingsview.cpp
index 2bc8e2c444d9d150aa7f9c0b470bf68c7028914e..6406f2854ee1c9913b9f3b08d8aba8cd13f5bca8 100644
--- a/src/mediasettingsview.cpp
+++ b/src/mediasettingsview.cpp
@@ -592,5 +592,4 @@ media_settings_view_show_preview(MediaSettingsView *self, gboolean show_preview)
         priv->cpp->avModel_->setAudioMeterState(false);
         priv->cpp->avModel_->stopAudioDevice();
     }
-
 }
diff --git a/src/ringmainwindow.cpp b/src/ringmainwindow.cpp
index 736961369cea1861086695e2b99e0d7f8a6f9e1c..8c56d400dd92992cd6f796a4983989b4c6aba3c8 100644
--- a/src/ringmainwindow.cpp
+++ b/src/ringmainwindow.cpp
@@ -1489,7 +1489,7 @@ GtkWidget*
 CppImpl::displayChatView(lrc::api::conversation::Info conversation, bool redraw_webview)
 {
     chatViewConversation_.reset(new lrc::api::conversation::Info(conversation));
-    auto* new_view = chat_view_new(webkitChatContainer(redraw_webview), accountInfo_, chatViewConversation_.get());
+    auto* new_view = chat_view_new(webkitChatContainer(redraw_webview), accountInfo_, chatViewConversation_.get(), lrc_->getAVModel());
     g_signal_connect_swapped(new_view, "hide-view-clicked", G_CALLBACK(on_hide_view_clicked), self);
     g_signal_connect(new_view, "add-conversation-clicked", G_CALLBACK(on_add_conversation_clicked), self);
     g_signal_connect(new_view, "place-audio-call-clicked", G_CALLBACK(on_place_audio_call_clicked), self);
diff --git a/src/webkitchatcontainer.cpp b/src/webkitchatcontainer.cpp
index f42d64636326da6ea9052498708185a78b01b315..5397e1a99d5c8a88d17e6cd8459418b2c0cc867d 100644
--- a/src/webkitchatcontainer.cpp
+++ b/src/webkitchatcontainer.cpp
@@ -761,6 +761,14 @@ webkit_chat_set_header_visible(WebKitChatContainer *view, bool isVisible)
     g_free(function_call);
 }
 
+void
+webkit_chat_set_record_visible(WebKitChatContainer *view, bool isVisible)
+{
+    gchar* function_call = g_strdup_printf("displayRecordControls(%s)", isVisible ? "true" : "false");
+    webkit_chat_container_execute_js(view, function_call);
+    g_free(function_call);
+}
+
 void
 webkit_chat_update_chatview_frame(WebKitChatContainer *view, bool accountEnabled, bool isBanned, bool isTemporary, const gchar* alias, const gchar* bestId)
 {
diff --git a/src/webkitchatcontainer.h b/src/webkitchatcontainer.h
index 45d46a8d66a42a0def6a261042da6301cdb4953c..6d182f644c4c3d7d6c4360fb04ee80e2f2536ad1 100644
--- a/src/webkitchatcontainer.h
+++ b/src/webkitchatcontainer.h
@@ -55,6 +55,7 @@ gboolean   webkit_chat_container_is_ready             (WebKitChatContainer *view
 void       webkit_chat_container_set_display_links    (WebKitChatContainer *view, bool display);
 void       webkit_chat_container_set_invitation       (WebKitChatContainer *view, bool show, const std::string& contactUri, const std::string& contactId);
 void       webkit_chat_set_header_visible             (WebKitChatContainer *view, bool isVisible);
+void       webkit_chat_set_record_visible             (WebKitChatContainer *view, bool isVisible);
 void       webkit_chat_update_chatview_frame          (WebKitChatContainer *view, bool accountEnabled, bool isBanned, bool isInvited, const gchar* alias, const gchar* bestId);
 
 G_END_DECLS
diff --git a/ui/chatview.ui b/ui/chatview.ui
index 7ec70fb59bfafd1599eab03577bbdf786da41215..eb67ffb62e0ab2aa600056bea3e79cf254b09147 100644
--- a/ui/chatview.ui
+++ b/ui/chatview.ui
@@ -4,101 +4,6 @@
   <template class="ChatView" parent="GtkBox">
     <property name="orientation">vertical</property>
 
-    <!-- chat info (only show for out of call conversations) -->
-    <child>
-      <object class="GtkBox" id="hbox_chat_info">
-        <property name="visible">False</property>
-        <property name="no-show-all">True</property>
-        <property name="orientation">horizontal</property>
-        <property name="spacing">5</property>
-        <child>
-          <object class="GtkButton" id="button_close_chatview">
-            <property name="image">image_back_arrow</property>
-            <property name="visible">True</property>
-            <property name="relief">none</property>
-            <property name="tooltip-text" translatable="yes">Hide chat view</property>
-            <child internal-child="accessible">
-              <object class="AtkObject" id="button_close_chatview-atkobject">
-                <property name="AtkObject::accessible-name" translatable="yes">Hide chat view</property>
-              </object>
-            </child>
-          </object>
-        </child>
-        <child>
-          <object class="GtkLabel" id="label_peer">
-            <property name="visible">True</property>
-            <property name="selectable">True</property>
-            <property name="ellipsize">end</property>
-            <attributes>
-              <attribute name="weight" value="bold"/>
-            </attributes>
-          </object>
-          <packing>
-            <property name="expand">False</property>
-            <property name="fill">True</property>
-          </packing>
-        </child>
-        <child>
-          <object class="GtkButton" id="button_placecall">
-            <property name="visible">True</property>
-            <property name="image">image_place_call</property>
-            <property name="tooltip-text" translatable="yes">Place call</property>
-            <child internal-child="accessible">
-              <object class="AtkObject" id="button_placecall-atkobject">
-                <property name="AtkObject::accessible-name" translatable="yes">Place call</property>
-              </object>
-            </child>
-          </object>
-          <packing>
-            <property name="pack-type">end</property>
-          </packing>
-        </child>
-        <child>
-          <object class="GtkButton" id="button_place_audio_call">
-            <property name="image">image_place_audio_only_call</property>
-            <property name="visible">True</property>
-            <property name="can_focus">True</property>
-            <property name="receives_default">True</property>
-            <property name="tooltip-text" translatable="yes">Place audio-only call</property>
-          </object>
-          <packing>
-            <property name="expand">False</property>
-            <property name="fill">True</property>
-            <property name="pack_type">end</property>
-            <property name="position">2</property>
-          </packing>
-        </child>
-        <child>
-          <object class="GtkButton" id="button_add_to_conversations">
-            <property name="visible">True</property>
-            <property name="image">image_invite</property>
-            <property name="tooltip-text" translatable="yes">Add to conversations</property>
-          </object>
-          <packing>
-            <property name="pack-type">end</property>
-          </packing>
-        </child>
-        <child>
-          <object class="GtkLabel" id="label_cm">
-            <property name="visible">True</property>
-            <property name="selectable">True</property>
-            <property name="ellipsize">end</property>
-            <attributes>
-              <attribute name="weight" value="bold"/>
-            </attributes>
-          </object>
-          <packing>
-            <property name="pack-type">end</property>
-          </packing>
-        </child>
-      </object>
-      <packing>
-        <property name="expand">False</property>
-        <property name="fill">True</property>
-      </packing>
-    </child>
-    <!-- end of chat info -->
-
     <!-- start of chat text view -->
     <child>
       <object class="GtkScrolledWindow" id="scrolledwindow_chat">