diff --git a/Ring/Ring.xcodeproj/project.pbxproj b/Ring/Ring.xcodeproj/project.pbxproj
index 8be49975ea23562f3518884681b91420e08e4727..21bbb6b4c92de72b914a1fa505123c0ba9476db2 100644
--- a/Ring/Ring.xcodeproj/project.pbxproj
+++ b/Ring/Ring.xcodeproj/project.pbxproj
@@ -247,6 +247,8 @@
 		263B7158246D9390007044C4 /* SmartListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 263B7157246D9390007044C4 /* SmartListCell.swift */; };
 		263B715A246D9556007044C4 /* IncognitoSmartListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 263B7159246D9556007044C4 /* IncognitoSmartListCell.swift */; };
 		263B715C246D96E5007044C4 /* IncognitoSmartListCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 263B715B246D96E5007044C4 /* IncognitoSmartListCell.xib */; };
+		2659F65827483656009107F1 /* VideoManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2659F65727483656009107F1 /* VideoManager.swift */; };
+		2659F65C27483A27009107F1 /* DecodingAdapterDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2659F65B27483A27009107F1 /* DecodingAdapterDelegate.swift */; };
 		2662FC79246B1E1700FA7782 /* JamiSearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2662FC78246B1E1700FA7782 /* JamiSearchView.swift */; };
 		2662FC7B246B216B00FA7782 /* JamiSearchViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2662FC7A246B216B00FA7782 /* JamiSearchViewModel.swift */; };
 		2662FC7D246B78E800FA7782 /* IncognitoSmartListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2662FC7C246B78E800FA7782 /* IncognitoSmartListViewController.swift */; };
@@ -661,6 +663,8 @@
 		263B7157246D9390007044C4 /* SmartListCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SmartListCell.swift; sourceTree = "<group>"; };
 		263B7159246D9556007044C4 /* IncognitoSmartListCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IncognitoSmartListCell.swift; sourceTree = "<group>"; };
 		263B715B246D96E5007044C4 /* IncognitoSmartListCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = IncognitoSmartListCell.xib; sourceTree = "<group>"; };
+		2659F65727483656009107F1 /* VideoManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoManager.swift; sourceTree = "<group>"; };
+		2659F65B27483A27009107F1 /* DecodingAdapterDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DecodingAdapterDelegate.swift; sourceTree = "<group>"; };
 		2662FC78246B1E1700FA7782 /* JamiSearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JamiSearchView.swift; sourceTree = "<group>"; };
 		2662FC7A246B216B00FA7782 /* JamiSearchViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JamiSearchViewModel.swift; sourceTree = "<group>"; };
 		2662FC7C246B78E800FA7782 /* IncognitoSmartListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = IncognitoSmartListViewController.swift; path = Ring/Features/Conversations/SmartList/IncognitoSmartListViewController.swift; sourceTree = SOURCE_ROOT; };
@@ -1015,6 +1019,8 @@
 				645BDD8024B74BCB009129B1 /* LocationSharingService.swift */,
 				26D08AB8269628F400E37574 /* RequestsService.swift */,
 				26D08ABA2696293100E37574 /* RequestsAdapterDelegate.swift */,
+				2659F65727483656009107F1 /* VideoManager.swift */,
+				2659F65B27483A27009107F1 /* DecodingAdapterDelegate.swift */,
 			);
 			path = Services;
 			sourceTree = "<group>";
@@ -2217,6 +2223,7 @@
 				5CE66F761FBF769B00EE9291 /* InitialLoadingViewController.swift in Sources */,
 				66ACB430214AE28C00A94162 /* ScanViewController.swift in Sources */,
 				0E6D959A2407115800996A28 /* LinkToAccountManagerViewController.swift in Sources */,
+				2659F65C27483A27009107F1 /* DecodingAdapterDelegate.swift in Sources */,
 				56BBC99F1ED714CB00CDAF8B /* ConversationsAdapter.mm in Sources */,
 				0E438A9A204F47E700402900 /* SettingsTableView.swift in Sources */,
 				0E49096A1FEAB156005CAA50 /* CallsAdapter.mm in Sources */,
@@ -2286,6 +2293,7 @@
 				1A20417C1F1E56FF00C08435 /* WelcomeViewModel.swift in Sources */,
 				1A5DC03D1F35678D0075E8EF /* RequestItem.swift in Sources */,
 				0E403F811F7D797300C80BC2 /* MessageCellGenerated.swift in Sources */,
+				2659F65827483656009107F1 /* VideoManager.swift in Sources */,
 				64F8127A24BBC19C00A7DE6A /* MessageCellLocationSharing.swift in Sources */,
 				0E6D959C2407116E00996A28 /* LinkToAccountManagerViewModel.swift in Sources */,
 				62AD584C2056DB2700AF0701 /* MessageCellDataTransferSent.swift in Sources */,
diff --git a/Ring/Ring/Account/VCardUtils.swift b/Ring/Ring/Account/VCardUtils.swift
index 236984ae91b9b7319dc3500763d95e72db55a3d6..4c5854b11fe80862adf27561f229f02c443878f9 100644
--- a/Ring/Ring/Account/VCardUtils.swift
+++ b/Ring/Ring/Account/VCardUtils.swift
@@ -102,7 +102,7 @@ class VCardUtils {
         return name
     }
 
-    class func sendVCard(card: CNContact, callID: String, accountID: String, sender: CallsService) {
+    class func sendVCard(card: CNContact, callID: String, accountID: String, sender: CallsService, from: String) {
         do {
             let vCard = card
             guard let vCardData = try CNContactVCardSerialization.dataWithImageAndUUID(from: vCard, andImageCompression: 40000, encoding: .utf8),
@@ -131,7 +131,7 @@ class VCardUtils {
                     chunk[key] = vCardString
                 }
                 i += 1
-                sender.sendChunk(callID: callID, message: chunk, accountId: accountID)
+                sender.sendChunk(callID: callID, message: chunk, accountId: accountID, from: from)
             }
         } catch {
             print(error)
diff --git a/Ring/Ring/AppDelegate.swift b/Ring/Ring/AppDelegate.swift
index aae55bd32d778a1db966dcee233469e72052e4de..71dab3251b00a6e4f44d88058483428fc3de37ea 100644
--- a/Ring/Ring/AppDelegate.swift
+++ b/Ring/Ring/AppDelegate.swift
@@ -45,6 +45,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
     private let callsProvider: CallsProviderDelegate = CallsProviderDelegate()
     private var conversationManager: ConversationsManager?
     private var interactionsManager: GeneratedInteractionsManager?
+    private var videoManager: VideoManager?
     private lazy var callService: CallsService = {
         CallsService(withCallsAdapter: CallsAdapter(), dbManager: self.dBManager)
     }()
@@ -152,8 +153,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
                                                         dataTransferService: self.dataTransferService,
                                                         callService: self.callService,
                                                         locationSharingService: self.locationSharingService, contactsService: self.contactsService,
-                                                        callsProvider: self.callsProvider,
-                                                        videoService: self.videoService, requestsService: self.requestsService)
+                                                        callsProvider: self.callsProvider, requestsService: self.requestsService)
+        self.videoManager = VideoManager(with: self.callService, callsProvider: self.callsProvider, videoService: self.videoService)
         self.window?.rootViewController = self.appCoordinator.rootViewController
         self.window?.makeKeyAndVisible()
 
diff --git a/Ring/Ring/Bridging/CallsAdapter.h b/Ring/Ring/Bridging/CallsAdapter.h
index 8f47337ad575403034d1135f9cc0e3b3893dd5ed..e433cd223df4a9cb3d3b0848e9608a02d6d212f5 100644
--- a/Ring/Ring/Bridging/CallsAdapter.h
+++ b/Ring/Ring/Bridging/CallsAdapter.h
@@ -27,32 +27,32 @@
 
 @property (class, nonatomic, weak) id <CallsAdapterDelegate> delegate;
 
-- (BOOL)acceptCallWithId:(NSString*)callId withMedia:(NSArray*)mediaList;
-- (BOOL)refuseCallWithId:(NSString*)callId;
-- (BOOL)hangUpCallWithId:(NSString*)callId;
-- (BOOL)holdCallWithId:(NSString*)callId;
-- (BOOL)unholdCallWithId:(NSString*)callId;
-
-- (BOOL)requestMediaChange:(NSString*)callId withMedia: (NSArray*)mediaList;
-- (void)answerMediaChangeResquest:(NSString*)callId withMedia: (NSArray*)mediaList;
-
-- (NSString*)placeCallWithAccountId:(NSString*)accountId toRingId:(NSString*)ringId withMedia: (NSArray*)mediaList;
-- (NSDictionary<NSString*,NSString*>*)callDetailsWithCallId:(NSString*)callId;
-- (NSArray<NSString*>*)calls;
-- (void) sendTextMessageWithCallID:(NSString*)callId message:(NSDictionary*)message accountId:(NSString*)accountId sMixed:(bool)isMixed;
-- (BOOL) muteMedia:(NSString*)callId mediaType:(NSString*)media muted:(bool)muted;
-- (void) playDTMF:(NSString*)code;
-
-- (BOOL)joinConference:(NSString*)confID call:(NSString*)callID;
-- (BOOL)joinConferences:(NSString*)firstConf secondConference:(NSString*)secondConf;
-- (BOOL)joinCall:(NSString*)firstCall second:(NSString*)secondCall;
-- (NSDictionary<NSString*,NSString*>*)getConferenceDetails:(NSString*)conferenceId;
-- (NSArray<NSString*>*)getConferenceCalls:(NSString*)conferenceId;
-- (BOOL)hangUpConference:(NSString*)conferenceId;
-- (void)setActiveParticipant:(NSString*)callId forConference:(NSString*)conferenceId;
-- (void)setConferenceLayout:(int)layout forConference:(NSString*)conferenceId;
-- (NSArray*)getConferenceInfo:(NSString*)conferenceId;
-- (void)setConferenceModerator:(NSString*)participantId forConference:(NSString*)conferenceId active:(BOOL)isActive;
-- (void)muteConferenceParticipant:(NSString*)participantId forConference:(NSString*)conferenceId active:(BOOL)isActive;
-- (void)hangupConferenceParticipant:(NSString*)participantId forConference:(NSString*)conferenceId;
+- (BOOL)acceptCallWithId:(NSString*)callId accountId:(NSString*)accountId withMedia:(NSArray*)mediaList;
+- (BOOL)refuseCallWithId:(NSString*)callId accountId:(NSString*)accountId;
+- (BOOL)hangUpCallWithId:(NSString*)callId accountId:(NSString*)accountId;
+- (BOOL)holdCallWithId:(NSString*)callId accountId:(NSString*)accountId;
+- (BOOL)unholdCallWithId:(NSString*)callId accountId:(NSString*)accountId;
+- (void)playDTMF:(NSString*)code;
+
+- (BOOL)requestMediaChange:(NSString*)callId accountId:(NSString*)accountId withMedia:(NSArray*)mediaList;
+- (void)answerMediaChangeResquest:(NSString*)callId accountId:(NSString*)accountId withMedia: (NSArray*)mediaList;
+
+- (NSString*)placeCallWithAccountId:(NSString*)accountId toParticipantId:(NSString*)participantId withMedia: (NSArray*)mediaList;
+- (NSDictionary<NSString*,NSString*>*)callDetailsWithCallId:(NSString*)callId accountId:(NSString*)accountId;
+- (NSArray<NSString*>*)callsForAccountId:(NSString*)accountId;
+- (void)sendTextMessageWithCallID:(NSString*)callId accountId:(NSString*)accountId message:(NSDictionary*)message from:(NSString*)jamiId isMixed:(bool)isMixed;
+- (BOOL)muteMedia:(NSString*)callId accountId:(NSString*)accountId mediaType:(NSString*)media muted:(bool)muted;
+
+- (BOOL)joinConference:(NSString*)confID call:(NSString*)callID accountId:(NSString*)accountId account2Id:(NSString*)account2Id;
+- (BOOL)joinConferences:(NSString*)firstConf secondConference:(NSString*)secondConf accountId:(NSString*)accountId account2Id:(NSString*)account2Id;
+- (BOOL)joinCall:(NSString*)firstCall second:(NSString*)secondCall accountId:(NSString*)accountId account2Id:(NSString*)account2Id;
+- (NSArray*)getConferenceInfo:(NSString*)conferenceId accountId:(NSString*)accountId;
+- (NSDictionary<NSString*,NSString*>*)getConferenceDetails:(NSString*)conferenceId accountId:(NSString*)accountId;
+- (NSArray<NSString*>*)getConferenceCalls:(NSString*)conferenceId accountId:(NSString*)accountId;
+- (BOOL)hangUpConference:(NSString*)conferenceId accountId:(NSString*)accountId;
+- (void)setActiveParticipant:(NSString*)callId forConference:(NSString*)conferenceId accountId:(NSString*)accountId;
+- (void)setConferenceLayout:(int)layout forConference:(NSString*)conferenceId accountId:(NSString*)accountId;
+- (void)setConferenceModerator:(NSString*)participantId forConference:(NSString*)conferenceId accountId:(NSString*)accountId active:(BOOL)isActive;
+- (void)muteConferenceParticipant:(NSString*)participantId forConference:(NSString*)conferenceId accountId:(NSString*)accountId active:(BOOL)isActive;
+- (void)hangupConferenceParticipant:(NSString*)participantId forConference:(NSString*)conferenceId accountId:(NSString*)accountId;
 @end
diff --git a/Ring/Ring/Bridging/CallsAdapter.mm b/Ring/Ring/Bridging/CallsAdapter.mm
index a1135db8d86ca35bd64b9d960f491da5307e05ed..949a7a2c593aa2343f954d9eb889bb2a7feb72c7 100644
--- a/Ring/Ring/Bridging/CallsAdapter.mm
+++ b/Ring/Ring/Bridging/CallsAdapter.mm
@@ -48,7 +48,7 @@ static id <CallsAdapterDelegate> _delegate;
     std::map<std::string, std::shared_ptr<CallbackWrapperBase>> callHandlers;
 
     //State changed signal
-    callHandlers.insert(exportable_callback<CallSignal::StateChange>([&](const std::string& callId,
+    callHandlers.insert(exportable_callback<CallSignal::StateChange>([&](const std::string& accountId, const std::string& callId,
                                                                          const std::string& state,
                                                                          int errorCode) {
         if (CallsAdapter.delegate) {
@@ -56,12 +56,14 @@ static id <CallsAdapterDelegate> _delegate;
             NSString* stateString = [NSString stringWithUTF8String:state.c_str()];
             [CallsAdapter.delegate didChangeCallStateWithCallId:callIdString
                                                           state:stateString
+                                                      accountId:[NSString stringWithUTF8String:accountId.c_str()]
                                                       stateCode:errorCode];
         }
     }));
 
     //Incoming message signal
-    callHandlers.insert(exportable_callback<CallSignal::IncomingMessage>([&](const std::string& callId,
+    callHandlers.insert(exportable_callback<CallSignal::IncomingMessage>([&](const std::string& accountId,
+                                                                             const std::string& callId,
                                                                              const std::string& fromURI,
                                                                              const std::map<std::string,
                                                                              std::string>& message) {
@@ -75,7 +77,7 @@ static id <CallsAdapterDelegate> _delegate;
                                                        message:messageDict];
         }
     }));
-    
+
     callHandlers.insert(exportable_callback<CallSignal::IncomingCallWithMedia>([&](const std::string& accountId,
                                                                                    const std::string& callId,
                                                                                    const std::string& fromURI,
@@ -141,22 +143,22 @@ static id <CallsAdapterDelegate> _delegate;
         }
     }));
 
-    callHandlers.insert(exportable_callback<CallSignal::ConferenceCreated>([&](const std::string& confId) {
+    callHandlers.insert(exportable_callback<CallSignal::ConferenceCreated>([&](const std::string& accountId, const std::string& confId) {
         if (CallsAdapter.delegate) {
             NSString* confIdString = [NSString stringWithUTF8String:confId.c_str()];
-            [CallsAdapter.delegate conferenceCreatedWithConference: confIdString];
+            [CallsAdapter.delegate conferenceCreatedWithConference: confIdString accountId:[NSString stringWithUTF8String:accountId.c_str()] ];
         }
     }));
 
-    callHandlers.insert(exportable_callback<CallSignal::ConferenceChanged>([&](const std::string& confId, const std::string& state) {
+    callHandlers.insert(exportable_callback<CallSignal::ConferenceChanged>([&](const std::string& accountId, const std::string& confId, const std::string& state) {
         if (CallsAdapter.delegate) {
             NSString* confIdString = [NSString stringWithUTF8String:confId.c_str()];
             NSString* stateString = [NSString stringWithUTF8String:state.c_str()];
-            [CallsAdapter.delegate conferenceChangedWithConference: confIdString state: stateString];
+            [CallsAdapter.delegate conferenceChangedWithConference: confIdString accountId: [NSString stringWithUTF8String:accountId.c_str()] state: stateString];
         }
     }));
 
-    callHandlers.insert(exportable_callback<CallSignal::ConferenceRemoved>([&](const std::string& confId) {
+    callHandlers.insert(exportable_callback<CallSignal::ConferenceRemoved>([&](const std::string& accountId, const std::string& confId) {
            if (CallsAdapter.delegate) {
                NSString* confIdString = [NSString stringWithUTF8String:confId.c_str()];
                [CallsAdapter.delegate conferenceRemovedWithConference: confIdString];
@@ -175,115 +177,116 @@ static id <CallsAdapterDelegate> _delegate;
 
 #pragma mark -
 
-- (BOOL)acceptCallWithId:(NSString*)callId withMedia:(NSArray*)mediaList {
-    return acceptWithMedia(std::string([callId UTF8String]), [Utils arrayOfDictionnarisToVectorOfMap: mediaList]);
+- (BOOL)acceptCallWithId:(NSString*)callId accountId:(NSString*)accountId withMedia:(NSArray*)mediaList {
+    return acceptWithMedia(std::string([accountId UTF8String]), std::string([callId UTF8String]), [Utils arrayOfDictionnarisToVectorOfMap: mediaList]);
 }
 
-- (BOOL)refuseCallWithId:(NSString*)callId {
-    return refuse(std::string([callId UTF8String]));
+- (BOOL)refuseCallWithId:(NSString*)callId accountId:(NSString*)accountId  {
+    return refuse(std::string([accountId UTF8String]), std::string([callId UTF8String]));
 }
 
-- (BOOL)hangUpCallWithId:(NSString*)callId {
-    return hangUp(std::string([callId UTF8String]));
+- (BOOL)hangUpCallWithId:(NSString*)callId accountId:(NSString*)accountId  {
+    return hangUp(std::string([accountId UTF8String]), std::string([callId UTF8String]));
 }
 
-- (BOOL)holdCallWithId:(NSString*)callId {
-    return hold(std::string([callId UTF8String]));
+- (BOOL)holdCallWithId:(NSString*)callId accountId:(NSString*)accountId  {
+    return hold(std::string([accountId UTF8String]), std::string([callId UTF8String]));
 }
 
-- (BOOL)unholdCallWithId:(NSString*)callId {
-    return unhold(std::string([callId UTF8String]));
+- (BOOL)unholdCallWithId:(NSString*)callId accountId:(NSString*)accountId  {
+    return unhold(std::string([accountId UTF8String]), std::string([callId UTF8String]));
 }
 
-- (void) playDTMF:(NSString*)code {
+- (void)playDTMF:(NSString*)code {
     playDTMF(std::string([code UTF8String]));
 }
 
-- (BOOL)requestMediaChange:(NSString*)callId withMedia: (NSArray*)mediaList {
-    requestMediaChange(std::string([callId UTF8String]), [Utils arrayOfDictionnarisToVectorOfMap: mediaList]);
+- (BOOL)requestMediaChange:(NSString*)callId accountId:(NSString*)accountId withMedia:(NSArray*)mediaList {
+    requestMediaChange(std::string([accountId UTF8String]), std::string([callId UTF8String]), [Utils arrayOfDictionnarisToVectorOfMap: mediaList]);
     return false;
 }
 
-- (void)answerMediaChangeResquest:(NSString*)callId withMedia: (NSArray*)mediaList {
-    answerMediaChangeRequest(std::string([callId UTF8String]), [Utils arrayOfDictionnarisToVectorOfMap: mediaList]);
+- (void)answerMediaChangeResquest:(NSString*)callId accountId:(NSString*)accountId withMedia: (NSArray*)mediaList {
+    answerMediaChangeRequest(std::string([accountId UTF8String]), std::string([callId UTF8String]), [Utils arrayOfDictionnarisToVectorOfMap: mediaList]);
 }
 
-- (NSString*)placeCallWithAccountId:(NSString*)accountId toRingId:(NSString*)ringId withMedia:(NSArray*)mediaList {
+- (NSString*)placeCallWithAccountId:(NSString*)accountId toParticipantId:(NSString*)participantId withMedia:(NSArray*)mediaList {
     std::string callId;
-    callId = placeCallWithMedia(std::string([accountId UTF8String]), std::string([ringId UTF8String]), [Utils arrayOfDictionnarisToVectorOfMap:mediaList]);
+    callId = placeCallWithMedia(std::string([accountId UTF8String]), std::string([participantId UTF8String]), [Utils arrayOfDictionnarisToVectorOfMap:mediaList]);
     return [NSString stringWithUTF8String:callId.c_str()];
 }
 
-- (void)sendTextMessageWithCallID:(NSString*)callId message:(NSDictionary*)message accountId:(NSString*)accountId sMixed:(bool)isMixed {
-    sendTextMessage(std::string([callId UTF8String]), [Utils dictionnaryToMap:message], std::string([accountId UTF8String]), isMixed);
-}
-
-- (NSDictionary<NSString*,NSString*>*)callDetailsWithCallId:(NSString*)callId {
-    std::map<std::string, std::string> callDetails = getCallDetails(std::string([callId UTF8String]));
+- (NSDictionary<NSString*,NSString*>*)callDetailsWithCallId:(NSString*)callId accountId:(NSString*)accountId {
+    std::map<std::string, std::string> callDetails = getCallDetails(std::string([accountId UTF8String]), std::string([callId UTF8String]));
     return [Utils mapToDictionnary:callDetails];
 }
 
-- (NSArray<NSString*>*)calls {
-    std::vector<std::string> calls = getCallList();
+- (NSArray<NSString*>*)callsForAccountId:(NSString*)accountId  {
+    std::vector<std::string> calls = getCallList(std::string([accountId UTF8String]));
     return [Utils vectorToArray:calls];
 }
 
-- (BOOL)muteMedia:(NSString*)callId mediaType:(NSString*)media muted:(bool)muted {
-    return muteLocalMedia(std::string([callId UTF8String]), std::string([media UTF8String]), muted);
+- (void)sendTextMessageWithCallID:(NSString*)callId accountId:(NSString*)accountId message:(NSDictionary*)message from:(NSString*)jamiId isMixed:(bool)isMixed {
+    sendTextMessage(std::string([accountId UTF8String]), std::string([callId UTF8String]), [Utils dictionnaryToMap:message], std::string([jamiId UTF8String]), isMixed);
 }
 
-- (BOOL)joinConference:(NSString*)confID call:(NSString*)callID {
-    return addParticipant(std::string([callID UTF8String]), std::string([confID UTF8String]));
+- (BOOL)muteMedia:(NSString*)callId accountId:(NSString*)accountId mediaType:(NSString*)media muted:(bool)muted {
+    return muteLocalMedia(std::string([accountId UTF8String]), std::string([callId UTF8String]), std::string([media UTF8String]), muted);
 }
 
-- (BOOL)joinCall:(NSString*)firstCall second:(NSString*)secondCall {
-    return joinParticipant(std::string([firstCall UTF8String]), std::string([secondCall UTF8String]));
+- (BOOL)joinConference:(NSString*)confID call:(NSString*)callID accountId:(NSString*)accountId account2Id:(NSString*)account2Id {
+    return addParticipant(std::string([accountId UTF8String]), std::string([callID UTF8String]), std::string([account2Id UTF8String]), std::string([confID UTF8String]));
 }
 
-- (BOOL)joinConferences:(NSString*)firstConf secondConference:(NSString*)secondConf {
-    return joinConference(std::string([firstConf UTF8String]), std::string([secondConf UTF8String]));
+- (BOOL)joinConferences:(NSString*)firstConf secondConference:(NSString*)secondConf accountId:(NSString*)accountId account2Id:(NSString*)account2Id {
+    return joinConference(std::string([accountId UTF8String]), std::string([firstConf UTF8String]), std::string([account2Id UTF8String]), std::string([secondConf UTF8String]));
 }
 
-- (BOOL)hangUpConference:(NSString*)conferenceId {
-    return hangUpConference(std::string([conferenceId UTF8String]));
+- (BOOL)joinCall:(NSString*)firstCall second:(NSString*)secondCall accountId:(NSString*)accountId account2Id:(NSString*)account2Id {
+    return joinParticipant(std::string([accountId UTF8String]), std::string([firstCall UTF8String]), std::string([account2Id UTF8String]), std::string([secondCall UTF8String]));
 }
 
-- (void)setActiveParticipant:(NSString*)callId forConference:(NSString*)conferenceId {
-    setActiveParticipant(std::string([conferenceId UTF8String]), std::string([callId UTF8String]));
+- (NSArray*)getConferenceInfo:(NSString*)conferenceId accountId:(NSString*)accountId {
+    auto result = getConferenceInfos(std::string([accountId UTF8String]), std::string([conferenceId UTF8String]));
+    NSArray* arrayResult = [Utils vectorOfMapsToArray:result];
+    return arrayResult;
 }
 
-- (void)setConferenceLayout:(int)layout forConference:(NSString*)conferenceId {
-    setConferenceLayout(std::string([conferenceId UTF8String]), layout);
+- (NSDictionary<NSString*,NSString*>*)getConferenceDetails:(NSString*)conferenceId accountId:(NSString*)accountId {
+    std::map<std::string, std::string> confDetails = getConferenceDetails(std::string([accountId UTF8String]), std::string([conferenceId UTF8String]));
+    return [Utils mapToDictionnary:confDetails];
 }
 
-- (void)setConferenceModerator:(NSString*)participantId forConference:(NSString*)conferenceId active:(BOOL)isActive {
-    setModerator(std::string([conferenceId UTF8String]), std::string([participantId UTF8String]), isActive);
+- (NSArray<NSString*>*)getConferenceCalls:(NSString*)conferenceId accountId:(NSString*)accountId {
+    std::vector<std::string> calls = getParticipantList(std::string([accountId UTF8String]), std::string([conferenceId UTF8String]));
+    return [Utils vectorToArray:calls];
 }
 
-- (void)muteConferenceParticipant:(NSString*)participantId forConference:(NSString*)conferenceId active:(BOOL)isActive {
-    muteParticipant(std::string([conferenceId UTF8String]), std::string([participantId UTF8String]), isActive);
+- (BOOL)hangUpConference:(NSString*)conferenceId accountId:(NSString*)accountId {
+    return hangUpConference(std::string([accountId UTF8String]), std::string([conferenceId UTF8String]));
 }
 
-- (void)hangupConferenceParticipant:(NSString*)participantId forConference:(NSString*)conferenceId {
-    hangupParticipant(std::string([conferenceId UTF8String]), std::string([participantId UTF8String]));
+- (void)setActiveParticipant:(NSString*)callId forConference:(NSString*)conferenceId accountId:(NSString*)accountId {
+    setActiveParticipant(std::string([accountId UTF8String]), std::string([conferenceId UTF8String]), std::string([callId UTF8String]));
 }
 
-- (NSArray*)getConferenceInfo:(NSString*)conferenceId {
-    auto result = getConferenceInfos(std::string([conferenceId UTF8String]));
-    NSArray* arrayResult = [Utils vectorOfMapsToArray:result];
-    return arrayResult;
+- (void)setConferenceLayout:(int)layout forConference:(NSString*)conferenceId accountId:(NSString*)accountId  {
+    setConferenceLayout(std::string([accountId UTF8String]), std::string([conferenceId UTF8String]), layout);
 }
 
-- (NSDictionary<NSString*,NSString*>*)getConferenceDetails:(NSString*)conferenceId {
-    std::map<std::string, std::string> confDetails = getConferenceDetails(std::string([conferenceId UTF8String]));
-    return [Utils mapToDictionnary:confDetails];
+- (void)setConferenceModerator:(NSString*)participantId forConference:(NSString*)conferenceId accountId:(NSString*)accountId active:(BOOL)isActive {
+    setModerator(std::string([accountId UTF8String]), std::string([conferenceId UTF8String]), std::string([participantId UTF8String]), isActive);
 }
 
-- (NSArray<NSString*>*)getConferenceCalls:(NSString*)conferenceId {
-    std::vector<std::string> calls = getParticipantList(std::string([conferenceId UTF8String]));
-    return [Utils vectorToArray:calls];
+- (void)muteConferenceParticipant:(NSString*)participantId forConference:(NSString*)conferenceId accountId:(NSString*)accountId active:(BOOL)isActive {
+    muteParticipant(std::string([accountId UTF8String]), std::string([conferenceId UTF8String]), std::string([participantId UTF8String]), isActive);
 }
 
+- (void)hangupConferenceParticipant:(NSString*)participantId forConference:(NSString*)conferenceId accountId:(NSString*)accountId {
+    hangupParticipant(std::string([accountId UTF8String]), std::string([conferenceId UTF8String]), std::string([participantId UTF8String]));
+}
+
+
 #pragma mark AccountAdapterDelegate
 
 + (id <CallsAdapterDelegate>)delegate {
diff --git a/Ring/Ring/Bridging/VideoAdapter.h b/Ring/Ring/Bridging/VideoAdapter.h
index c17ede31049f67cc92ddefc08f85a85a85f06914..446d7bf8bac4d3e39e83b2b09417f5e4e44ea0be 100644
--- a/Ring/Ring/Bridging/VideoAdapter.h
+++ b/Ring/Ring/Bridging/VideoAdapter.h
@@ -23,10 +23,12 @@
 #import <AVFoundation/AVFoundation.h>
 
 @protocol VideoAdapterDelegate;
+@protocol DecodingAdapterDelegate;
 
 @interface VideoAdapter : NSObject
 
-@property (class, nonatomic, weak) id <VideoAdapterDelegate> delegate;
+@property (class, nonatomic, weak) id <VideoAdapterDelegate> videoDelegate;
+@property (class, nonatomic, weak) id <DecodingAdapterDelegate> decodingDelegate;
 
 - (void)addVideoDeviceWithName:(NSString*)deviceName withDevInfo:(NSDictionary*)deviceInfoDict;
 - (void)setDefaultDevice:(NSString*)deviceName;
@@ -36,18 +38,18 @@
                           withHeight:(NSInteger)h;
 - (void)removeSinkTargetWithSinkId:(NSString*)sinkId;
 - (void)writeOutgoingFrameWithBuffer:(CVImageBufferRef)image
-                               angle:(int)angle;
+                               angle:(int)angle
+                        videoInputId:(NSString*)videoInputId;
 - (void)setDecodingAccelerated:(BOOL)state;
 - (BOOL)getDecodingAccelerated;
-- (void)switchInput:(NSString*)deviceName;
-- (void)switchInput:(NSString*)deviceName forCall:(NSString*) callID;
+- (void)switchInput:(NSString*)videoInputId accountId:(NSString*)accountId forCall:(NSString*)callID;
 - (void)setEncodingAccelerated:(BOOL)state;
 - (BOOL)getEncodingAccelerated;
 - (void)stopAudioDevice;
-- (NSString*)startLocalRecording:(NSString*) path audioOnly:(BOOL)audioOnly;
+- (NSString*)startLocalRecording:(NSString*)videoInputId path:(NSString*)path;
 - (void)stopLocalRecording:(NSString*) path;
-- (void)startCamera;
-- (void)stopCamera;
+- (void)openVideoInput:(NSString*)path;
+- (void)closeVideoInput:(NSString*)path;
 - (NSString*)createMediaPlayer:(NSString*)path;
 - (bool)pausePlayer:(NSString*)playerId pause:(BOOL)pause;
 - (bool)closePlayer:(NSString*)playerId;
diff --git a/Ring/Ring/Bridging/VideoAdapter.mm b/Ring/Ring/Bridging/VideoAdapter.mm
index 528474bd3795f39bc627fc909276fe6342a71c65..49f69b22a35d9e57304d1790e599cd69926cd753 100644
--- a/Ring/Ring/Bridging/VideoAdapter.mm
+++ b/Ring/Ring/Bridging/VideoAdapter.mm
@@ -46,14 +46,14 @@ struct Renderer
 
     void bindAVSinkFunctions() {
         avtarget.push = [this](std::unique_ptr<DRing::VideoFrame> frame) {
-            if(!VideoAdapter.delegate) {
+            if(!VideoAdapter.videoDelegate) {
                 return;
             }
             @autoreleasepool {
                 UIImage *image = [Utils
                                   convertHardwareDecodedFrameToImage: std::move(frame->pointer())];
                 isRendering = true;
-                [VideoAdapter.delegate writeFrameWithImage: image forCallId: rendererId];
+                [VideoAdapter.videoDelegate writeFrameWithImage: image forCallId: rendererId];
                 isRendering = false;
             }
         };
@@ -72,7 +72,7 @@ struct Renderer
         target.push = [this](DRing::SinkTarget::FrameBufferPtr buf) {
             std::lock_guard<std::mutex> lk(renderMutex);
             daemonFramePtr_ = std::move(buf);
-            if(VideoAdapter.delegate) {
+            if(VideoAdapter.videoDelegate) {
                 @autoreleasepool {
                     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
                     CGContextRef bitmapContext = CGBitmapContextCreate((void *)daemonFramePtr_->ptr,
@@ -88,7 +88,7 @@ struct Renderer
                     UIImage* image = [UIImage imageWithCGImage:cgImage];
                     CGImageRelease(cgImage);
                     isRendering = true;
-                    [VideoAdapter.delegate writeFrameWithImage: image forCallId: rendererId];
+                    [VideoAdapter.videoDelegate writeFrameWithImage: image forCallId: rendererId];
                     isRendering = false;
                 }
             }
@@ -101,8 +101,9 @@ struct Renderer
     std::map<std::string, std::shared_ptr<Renderer>> renderers;
 }
 
-// Static delegate that will receive the propagated daemon events
-static id <VideoAdapterDelegate> _delegate;
+// Static delegates that will receive the propagated daemon events
+static id <VideoAdapterDelegate> _videoDelegate;
+static id <DecodingAdapterDelegate> _decodingDelegate;
 
 #pragma mark Init
 
@@ -125,44 +126,39 @@ static id <VideoAdapterDelegate> _delegate;
                                                                                int w,
                                                                                int h,
                                                                                bool is_mixer) {
-        if(VideoAdapter.delegate) {
+        if(VideoAdapter.decodingDelegate) {
             NSString* rendererId = [NSString stringWithUTF8String:renderer_id.c_str()];
-            NSString* codecName = @"";
-            std::map<std::string, std::string> callDetails = getCallDetails(renderer_id);
-            if (callDetails.find("VIDEO_CODEC") != callDetails.end()) {
-                codecName = [NSString stringWithUTF8String: callDetails["VIDEO_CODEC"].c_str()];
-            }
-            [VideoAdapter.delegate decodingStartedWithRendererId:rendererId withWidth:(NSInteger)w withHeight:(NSInteger)h withCodec: codecName];
+            [VideoAdapter.decodingDelegate decodingStartedWithRendererId:rendererId withWidth:(NSInteger)w withHeight:(NSInteger)h];
         }
     }));
 
     videoHandlers.insert(exportable_callback<VideoSignal::DecodingStopped>([&](const std::string& renderer_id,
                                                                                const std::string& shm_path,
                                                                                bool is_mixer) {
-        if(VideoAdapter.delegate) {
+        if(VideoAdapter.decodingDelegate) {
             NSString* rendererId = [NSString stringWithUTF8String:renderer_id.c_str()];
-            [VideoAdapter.delegate decodingStoppedWithRendererId:rendererId];
+            [VideoAdapter.decodingDelegate decodingStoppedWithRendererId:rendererId];
         }
     }));
 
     videoHandlers.insert(exportable_callback<VideoSignal::StartCapture>([&](const std::string& device) {
-        if(VideoAdapter.delegate) {
+        if(VideoAdapter.videoDelegate) {
             NSString* deviceString = [NSString stringWithUTF8String:device.c_str()];
-            [VideoAdapter.delegate startCaptureWithDevice:deviceString];
+            [VideoAdapter.videoDelegate startCaptureWithDevice:deviceString];
         }
     }));
 
-    videoHandlers.insert(exportable_callback<VideoSignal::StopCapture>([&]() {
-        if(VideoAdapter.delegate) {
-            [VideoAdapter.delegate stopCapture];
+    videoHandlers.insert(exportable_callback<VideoSignal::StopCapture>([&](const std::string& deviceId) {
+        if(VideoAdapter.videoDelegate) {
+            [VideoAdapter.videoDelegate stopCapture];
         }
     }));
 
     videoHandlers.insert(exportable_callback<MediaPlayerSignal::FileOpened>([&](const std::string& playerId, std::map<std::string, std::string> playerInfo) {
-        if(VideoAdapter.delegate) {
+        if(VideoAdapter.videoDelegate) {
             NSString* player = @(playerId.c_str());
             NSMutableDictionary* info = [Utils mapToDictionnary:playerInfo];
-            [VideoAdapter.delegate fileOpenedFor:player fileInfo:info];
+            [VideoAdapter.videoDelegate fileOpenedFor:player fileInfo:info];
         }
     }));
 
@@ -211,8 +207,10 @@ static id <VideoAdapterDelegate> _delegate;
 }
 
 - (void)writeOutgoingFrameWithBuffer:(CVImageBufferRef)image
-                               angle:(int)angle{
-    auto frame = DRing::getNewFrame();
+                               angle:(int)angle
+                        videoInputId:(NSString*)videoInputId
+{
+    auto frame = DRing::getNewFrame(std::string([videoInputId UTF8String]));
     if(!frame) {
         return;
     }
@@ -221,7 +219,7 @@ static id <VideoAdapterDelegate> _delegate;
               fromImageBuffer:image
                         angle:(int) angle];
 
-    DRing::publishFrame();
+    DRing::publishFrame(std::string([videoInputId UTF8String]));
 }
 
 - (void)addVideoDeviceWithName:(NSString*)deviceName withDevInfo:(NSDictionary*)deviceInfoDict {
@@ -254,20 +252,16 @@ static id <VideoAdapterDelegate> _delegate;
     return DRing::getEncodingAccelerated();
 }
 
-- (void)switchInput:(NSString*)deviceName {
-    DRing::switchInput(std::string([deviceName UTF8String]));
-}
-
-- (void)switchInput:(NSString*)deviceName forCall:(NSString*) callID {
-    DRing::switchInput(std::string([callID UTF8String]), std::string([deviceName UTF8String]));
+- (void)switchInput:(NSString*)videoInputId accountId:(NSString*)accountId forCall:(NSString*)callID {
+    DRing::switchInput(std::string([accountId UTF8String]), std::string([callID UTF8String]), std::string([videoInputId UTF8String]));
 }
 
 - (void)stopAudioDevice {
     DRing::stopAudioDevice();
 }
 
-- (NSString* )startLocalRecording:(NSString*) path audioOnly:(BOOL)audioOnly {
-    return @(DRing::startLocalRecorder(audioOnly, std::string([path UTF8String])).c_str());
+- (NSString*)startLocalRecording:(NSString*)videoInputId path:(NSString*)path {
+    return @(DRing::startLocalMediaRecorder(std::string([videoInputId UTF8String]), std::string([path UTF8String])).c_str());
 }
 
 - (void)stopLocalRecording:(NSString*) path {
@@ -281,8 +275,8 @@ static id <VideoAdapterDelegate> _delegate;
     return DRing::pausePlayer(std::string([playerId UTF8String]), pause);
 }
 
-- (bool)closePlayer:(NSString*)playerId {
-    return DRing::closePlayer(std::string([playerId UTF8String]));
+-(bool)closePlayer:(NSString*)playerId {
+    return DRing::closeMediaPlayer(std::string([playerId UTF8String]));
 }
 
 - (bool)mutePlayerAudio:(NSString*)playerId mute:(BOOL)mute {
@@ -297,22 +291,32 @@ static id <VideoAdapterDelegate> _delegate;
     return DRing::getPlayerPosition(std::string([playerId UTF8String]));
 }
 
-- (void)startCamera {
-    DRing::startCamera();
+- (void)openVideoInput:(NSString*)path {
+    DRing::openVideoInput(std::string([path UTF8String]));
+}
+
+- (void)closeVideoInput:(NSString*)path {
+    DRing::closeVideoInput(std::string([path UTF8String]));
+}
+
+#pragma mark VideoAdapterDelegate
+
++ (id <VideoAdapterDelegate>)videoDelegate {
+    return _videoDelegate;
 }
 
-- (void)stopCamera {
-    DRing::stopCamera();
++ (void) setVideoDelegate:(id<VideoAdapterDelegate>)videoDelegate {
+    _videoDelegate = videoDelegate;
 }
 
-#pragma mark PresenceAdapterDelegate
+#pragma mark DecodingAdapterDelegate
 
-+ (id <VideoAdapterDelegate>)delegate {
-    return _delegate;
++ (id <DecodingAdapterDelegate>)decodingDelegate {
+    return _decodingDelegate;
 }
 
-+ (void) setDelegate:(id<VideoAdapterDelegate>)delegate {
-    _delegate = delegate;
++ (void) setDecodingDelegate:(id<DecodingAdapterDelegate>)decodingDelegate {
+    _decodingDelegate = decodingDelegate;
 }
 
 #pragma mark -
diff --git a/Ring/Ring/Calls/CallViewModel.swift b/Ring/Ring/Calls/CallViewModel.swift
index 9dfbc6e549096e66c8347a7fcf517fda19b71862..e1d50071e3a7fcb01721e2ea9b636c4f91a32fc1 100644
--- a/Ring/Ring/Calls/CallViewModel.swift
+++ b/Ring/Ring/Calls/CallViewModel.swift
@@ -581,7 +581,7 @@ extension CallViewModel {
             }
             guard let secondCall = self.callService.call(callID: contact.conferenceID) else { return }
             if call.participantsCallId.count == 1 {
-                self.callService.joinCall(firstCall: call.callId, secondCall: secondCall.callId)
+                self.callService.joinCall(firstCallId: call.callId, secondCallId: secondCall.callId)
             } else {
                 self.callService.joinConference(confID: contact.conferenceID, callID: self.rendererId)
             }
@@ -752,10 +752,9 @@ extension CallViewModel {
         guard let account = self.accountService.currentAccount else { return false }
         return self.callService.isModerator(participantId: account.jamiId, inConference: self.rendererId)
     }
-
     func getItemsForConferenceMenu(participantId: String, callId: String) -> [MenuItem] {
         let conference = self.callService.call(callID: self.rendererId)
-        let active = self.callService.isParticipant(participantURI: participantId, activeIn: self.rendererId)
+        let active = self.callService.isParticipant(participantURI: participantId, activeIn: self.rendererId, accountId: conference?.accountId ?? "")
         // menu for local call
         if self.isLocalCall(participantId: participantId) || participantId.isEmpty {
             return menuItemsManager.getMenuItemsForLocalCall(conference: conference, active: active)
diff --git a/Ring/Ring/Features/Conversations/SendFile/SendFileViewModel.swift b/Ring/Ring/Features/Conversations/SendFile/SendFileViewModel.swift
index c6117cfb6ccc92e302ecb47eb1878c946db2164a..b02db5b2304348d0138cd44304989ff67d1a32da 100644
--- a/Ring/Ring/Features/Conversations/SendFile/SendFileViewModel.swift
+++ b/Ring/Ring/Features/Conversations/SendFile/SendFileViewModel.swift
@@ -131,7 +131,7 @@ class SendFileViewModel: Stateable, ViewModel {
         self.injectionBag = injectionBag
         if !audioOnly {
             videoService.setCameraOrientation(orientation: UIDevice.current.orientation)
-            videoService.startCamera()
+            videoService.startMediumCamera()
         }
         videoService.capturedVideoFrame.asObservable()
             .subscribe(onNext: { [weak self] frame in
@@ -163,7 +163,10 @@ class SendFileViewModel: Stateable, ViewModel {
         let dateString = dateFormatter.string(from: date)
         let random = String(arc4random_uniform(9999))
         let nameForRecordingFile = dateString + "_" + random
-        guard let url = self.fileTransferService.getFilePathForRecordings(forFile: nameForRecordingFile, accountID: conversation.accountId, conversationID: conversation.id, isSwarm: self.conversation.isSwarm()) else { return }
+        guard let url = self.fileTransferService.getFilePathForRecordings(forFile: nameForRecordingFile,
+                                                                          accountID: conversation.accountId,
+                                                                          conversationID: conversation.id,
+                                                                          isSwarm: self.conversation.isSwarm()) else { return }
         guard let name = self.videoService
             .startLocalRecorder(audioOnly: audioOnly, path: url.path) else {
                 return
diff --git a/Ring/Ring/Models/CallModel.swift b/Ring/Ring/Models/CallModel.swift
index 86862cde7e0e736d7c8f5dec8ace1d8da406138d..2ed9a210b0770c1676cf33c820792246d5b1bb67 100644
--- a/Ring/Ring/Models/CallModel.swift
+++ b/Ring/Ring/Models/CallModel.swift
@@ -68,6 +68,7 @@ enum CallDetailKey: String {
     case videoSourceKey = "VIDEO_SOURCE"
     case audioOnlyKey = "AUDIO_ONLY"
     case confID = "CONF_ID"
+    case videoCodec = "VIDEO_CODEC"
 }
 
 enum MediaAttributeKey: String {
diff --git a/Ring/Ring/Services/CallsAdapterDelegate.swift b/Ring/Ring/Services/CallsAdapterDelegate.swift
index 9743f947b63dafa7f80dbc95617e8a95f4865759..07a5cd19ced68da8a43e1e51ced1002864e57cfc 100644
--- a/Ring/Ring/Services/CallsAdapterDelegate.swift
+++ b/Ring/Ring/Services/CallsAdapterDelegate.swift
@@ -20,15 +20,15 @@
  */
 
 @objc protocol CallsAdapterDelegate {
-    func didChangeCallState(withCallId callId: String, state: String, stateCode: NSInteger)
+    func didChangeCallState(withCallId callId: String, state: String, accountId: String, stateCode: NSInteger)
     func didReceiveMediaChangeRequest(withAccountId accountId: String, callId: String, withMedia: [[String: String]])
     func didReceiveMessage(withCallId callId: String, fromURI uri: String, message: [String: String])
     func receivingCall(withAccountId accountId: String, callId: String, fromURI uri: String, withMedia: [[String: String]])
     func callPlacedOnHold(withCallId callId: String, holding: Bool)
     func audioMuted(call callId: String, mute: Bool)
     func videoMuted(call callId: String, mute: Bool)
-    func conferenceCreated(conference conferenceID: String)
-    func conferenceChanged(conference conferenceID: String, state: String)
+    func conferenceCreated(conference conferenceID: String, accountId: String)
+    func conferenceChanged(conference conferenceID: String, accountId: String, state: String)
     func conferenceRemoved(conference conferenceID: String)
     func conferenceInfoUpdated(conference conferenceID: String, info: [[String: String]])
     func didChangeMediaNegotiationStatus(withCallId callId: String, event: String, withMedia: [[String: String]])
diff --git a/Ring/Ring/Services/CallsService.swift b/Ring/Ring/Services/CallsService.swift
index 67e01bcd7b1b2601a5d87a3947d2f510625c1f36..084c0279a0fb8eed4c58c51e92f334a1f6048de2 100644
--- a/Ring/Ring/Services/CallsService.swift
+++ b/Ring/Ring/Services/CallsService.swift
@@ -56,6 +56,7 @@ class CallsService: CallsAdapterDelegate {
 
     var calls = BehaviorRelay<[String: CallModel]>(value: [String: CallModel]())
     var pendingConferences = [String: Set<String>]()
+    var createdConferences = Set<String>() /// set of created conferences, waiting to calls to be attached
 
     private let ringVCardMIMEType = "x-ring/ring.profile.vcard;"
 
@@ -102,15 +103,15 @@ class CallsService: CallsAdapterDelegate {
 
     @objc
     func refuseUnansweredCall(_ notification: NSNotification) {
-        guard let callid = notification.userInfo?[NotificationUserInfoKeys.callID.rawValue] as? String else {
+        guard let callId = notification.userInfo?[NotificationUserInfoKeys.callID.rawValue] as? String else {
             return
         }
-        guard let call = self.call(callID: callid) else {
+        guard let call = self.call(callID: callId) else {
             return
         }
 
         if call.state == .incoming {
-            self.refuse(callId: callid)
+            self.refuse(callId: callId)
                 .subscribe({_ in
                     print("Call ignored")
                 })
@@ -122,6 +123,11 @@ class CallsService: CallsAdapterDelegate {
         return self.calls.value[callID]
     }
 
+    func getVideoCodec(call: CallModel) -> String? {
+        let callDetails = self.callsAdapter.callDetails(withCallId: call.callId, accountId: call.accountId)
+        return callDetails?[CallDetailKey.videoCodec.rawValue]
+    }
+
     func call(participantHash: String, accountID: String) -> CallModel? {
         return self.calls
             .value.values
@@ -137,7 +143,7 @@ class CallsService: CallsAdapterDelegate {
                 completable(.error(CallServiceError.acceptCallFailed))
                 return Disposables.create { }
             }
-            let success = self.callsAdapter.acceptCall(withId: callId, withMedia: call?.mediaList)
+            let success = self.callsAdapter.acceptCall(withId: callId, accountId: call?.accountId, withMedia: call?.mediaList)
             if success {
                 completable(.completed)
             } else {
@@ -149,30 +155,33 @@ class CallsService: CallsAdapterDelegate {
 
     func joinConference(confID: String, callID: String) {
         guard let secondConf = self.call(callID: callID) else { return }
+        guard let firstConf = self.call(callID: confID) else { return }
         if let pending = self.pendingConferences[confID], !pending.isEmpty {
             self.pendingConferences[confID]!.insert(callID)
         } else {
             self.pendingConferences[confID] = [callID]
         }
         if secondConf.participantsCallId.count == 1 {
-            self.callsAdapter.joinConference(confID, call: callID)
+            self.callsAdapter.joinConference(confID, call: callID, accountId: firstConf.accountId, account2Id: secondConf.accountId)
         } else {
-            self.callsAdapter.joinConferences(confID, secondConference: callID)
+            self.callsAdapter.joinConferences(confID, secondConference: callID, accountId: firstConf.accountId, account2Id: secondConf.accountId)
         }
     }
 
-    func joinCall(firstCall: String, secondCall: String) {
-        if let pending = self.pendingConferences[firstCall], !pending.isEmpty {
-            self.pendingConferences[firstCall]!.insert(secondCall)
+    func joinCall(firstCallId: String, secondCallId: String) {
+        guard let firstCall = self.call(callID: firstCallId) else { return }
+        guard let secondCall = self.call(callID: secondCallId) else { return }
+        if let pending = self.pendingConferences[firstCallId], !pending.isEmpty {
+            self.pendingConferences[firstCallId]!.insert(secondCallId)
         } else {
-            self.pendingConferences[firstCall] = [secondCall]
+            self.pendingConferences[firstCallId] = [secondCallId]
         }
-        self.callsAdapter.joinCall(firstCall, second: secondCall)
+        self.callsAdapter.joinCall(firstCallId, second: secondCallId, accountId: firstCall.accountId, account2Id: secondCall.accountId)
     }
 
-    func isParticipant(participantURI: String?, activeIn conferenceId: String) -> Bool? {
+    func isParticipant(participantURI: String?, activeIn conferenceId: String, accountId: String) -> Bool? {
         guard let uri = participantURI,
-            let participantsArray = self.callsAdapter.getConferenceInfo(conferenceId) as? [[String: String]] else { return nil }
+              let participantsArray = self.callsAdapter.getConferenceInfo(conferenceId, accountId: accountId) as? [[String: String]] else { return nil }
         let participants = self.arrayToConferenceParticipants(participants: participantsArray, onlyURIAndActive: true)
         for participant in participants where participant.uri?.filterOutHost() == uri.filterOutHost() {
             return participant.isActive
@@ -210,11 +219,11 @@ class CallsService: CallsAdapterDelegate {
 
     func setActiveParticipant(conferenceId: String, maximixe: Bool, jamiId: String) {
         guard let conference = self.call(callID: conferenceId),
-              let isActive = self.isParticipant(participantURI: jamiId, activeIn: conferenceId) else { return }
+              let isActive = self.isParticipant(participantURI: jamiId, activeIn: conferenceId, accountId: conference.accountId) else { return }
         let newLayout = isActive ? self.getNewLayoutForActiveParticipant(currentLayout: conference.layout, maximixe: maximixe) : .oneWithSmal
         conference.layout = newLayout
-        self.callsAdapter.setActiveParticipant(jamiId, forConference: conferenceId)
-        self.callsAdapter.setConferenceLayout(newLayout.rawValue, forConference: conferenceId)
+        self.callsAdapter.setActiveParticipant(jamiId, forConference: conferenceId, accountId: conference.accountId)
+        self.callsAdapter.setConferenceLayout(newLayout.rawValue, forConference: conferenceId, accountId: conference.accountId)
     }
 
     private func getNewLayoutForActiveParticipant(currentLayout: CallLayout, maximixe: Bool) -> CallLayout {
@@ -236,7 +245,13 @@ class CallsService: CallsAdapterDelegate {
                                userName: String,
                                isAudioOnly: Bool = false) -> Observable<CallModel> {
         let call = self.calls.value[callId]
-        let placeCall = self.placeCall(withAccount: account, toRingId: contactId, userName: userName, isAudioOnly: isAudioOnly, withMedia: call?.mediaList ?? [[String: String]]()).asObservable().publish()
+        let placeCall = self.placeCall(withAccount: account,
+                                       toRingId: contactId,
+                                       userName: userName,
+                                       isAudioOnly: isAudioOnly,
+                                       withMedia: call?.mediaList ?? [[String: String]]())
+            .asObservable()
+            .publish()
         placeCall
             .subscribe(onNext: { (callModel) in
                 self.inConferenceCalls.onNext(callModel)
@@ -253,7 +268,11 @@ class CallsService: CallsAdapterDelegate {
 
     func refuse(callId: String) -> Completable {
         return Completable.create(subscribe: { completable in
-            let success = self.callsAdapter.refuseCall(withId: callId)
+            guard let call = self.call(callID: callId) else {
+                completable(.error(CallServiceError.hangUpCallFailed))
+                return Disposables.create { }
+            }
+            let success = self.callsAdapter.refuseCall(withId: callId, accountId: call.accountId)
             if success {
                 completable(.completed)
             } else {
@@ -266,7 +285,11 @@ class CallsService: CallsAdapterDelegate {
     func hangUp(callId: String) -> Completable {
         return Completable.create(subscribe: { completable in
             var success: Bool
-                success = self.callsAdapter.hangUpCall(withId: callId)
+            guard let call = self.call(callID: callId) else {
+                completable(.error(CallServiceError.hangUpCallFailed))
+                return Disposables.create { }
+            }
+            success = self.callsAdapter.hangUpCall(withId: callId, accountId: call.accountId)
             if success {
                 completable(.completed)
             } else {
@@ -284,9 +307,9 @@ class CallsService: CallsAdapterDelegate {
                 }
                 var success: Bool
                 if call.participantsCallId.count < 2 {
-                    success = self.callsAdapter.hangUpCall(withId: callId)
+                    success = self.callsAdapter.hangUpCall(withId: callId, accountId: call.accountId)
                 } else {
-                    success = self.callsAdapter.hangUpConference(callId)
+                    success = self.callsAdapter.hangUpConference(callId, accountId: call.accountId)
                 }
                 if success {
                     completable(.completed)
@@ -299,7 +322,11 @@ class CallsService: CallsAdapterDelegate {
 
     func hold(callId: String) -> Completable {
         return Completable.create(subscribe: { completable in
-            let success = self.callsAdapter.holdCall(withId: callId)
+            guard let call = self.call(callID: callId) else {
+                completable(.error(CallServiceError.hangUpCallFailed))
+                return Disposables.create { }
+            }
+            let success = self.callsAdapter.holdCall(withId: callId, accountId: call.accountId)
             if success {
                 completable(.completed)
             } else {
@@ -311,7 +338,11 @@ class CallsService: CallsAdapterDelegate {
 
     func unhold(callId: String) -> Completable {
         return Completable.create(subscribe: { completable in
-            let success = self.callsAdapter.unholdCall(withId: callId)
+            guard let call = self.call(callID: callId) else {
+                completable(.error(CallServiceError.hangUpCallFailed))
+                return Disposables.create { }
+            }
+            let success = self.callsAdapter.unholdCall(withId: callId, accountId: call.accountId)
             if success {
                 completable(.completed)
             } else {
@@ -355,9 +386,9 @@ class CallsService: CallsAdapterDelegate {
         call.callType = .outgoing
         return Single<CallModel>.create(subscribe: { [weak self] single in
             if let self = self, let callId = self.callsAdapter.placeCall(withAccountId: account.id,
-                                                                         toRingId: ringId,
+                                                                         toParticipantId: ringId,
                                                                          withMedia: mediaList),
-                let callDictionary = self.callsAdapter.callDetails(withCallId: callId) {
+               let callDictionary = self.callsAdapter.callDetails(withCallId: callId, accountId: account.id) {
                 call.update(withDictionary: callDictionary, withMedia: mediaList)
                 call.callId = callId
                 call.participantsCallId.removeAll()
@@ -375,8 +406,10 @@ class CallsService: CallsAdapterDelegate {
     }
 
     func hostMuteAudio(conferenceId: String, mute: Bool, localCallId: String) {
+        guard let conference = call(callID: conferenceId) else { return }
         let success = self.callsAdapter
             .muteMedia(conferenceId,
+                       accountId: conference.accountId,
                        mediaType: String(describing: MediaType.audio),
                        muted: mute)
         guard let call = self.calls.value[localCallId], success else {
@@ -387,8 +420,11 @@ class CallsService: CallsAdapterDelegate {
     }
 
     func hostMuteVideo(conferenceId: String, mute: Bool, localCallId: String) {
+        guard let conference = self.calls.value[conferenceId] else {
+            return
+        }
         let success = self.callsAdapter
-            .muteMedia(conferenceId,
+            .muteMedia(conferenceId, accountId: conference.accountId,
                        mediaType: String(describing: MediaType.video),
                        muted: mute)
         guard let call = self.calls.value[localCallId], success else {
@@ -421,8 +457,8 @@ class CallsService: CallsAdapterDelegate {
             media[MediaAttributeKey.label.rawValue] = mediaLabel
             mediaList.append(media)
         }
-        self.callsAdapter.requestMediaChange(callId, withMedia: mediaList)
-        if let callDictionary = self.callsAdapter.callDetails(withCallId: callId) {
+        self.callsAdapter.requestMediaChange(callId, accountId: call.accountId, withMedia: mediaList)
+        if let callDictionary = self.callsAdapter.callDetails(withCallId: callId, accountId: call.accountId) {
             call.update(withDictionary: callDictionary, withMedia: mediaList)
             self.currentCallsEvents.onNext(call)
         }
@@ -431,7 +467,7 @@ class CallsService: CallsAdapterDelegate {
     func muteCurrentCallVideoVideo(mute: Bool) {
         for call in self.calls.value.values where call.state == .current {
                 self.callsAdapter
-                    .muteMedia(call.callId,
+                    .muteMedia(call.callId, accountId: call.accountId,
                                mediaType: String(describing: MediaType.video),
                                muted: mute)
                 return
@@ -446,13 +482,14 @@ class CallsService: CallsAdapterDelegate {
         if accountID.isEmpty || callID.isEmpty {
             return
         }
-        guard let vCard = self.dbManager.accountVCard(for: accountID) else { return }
+        guard let vCard = self.dbManager.accountVCard(for: accountID),
+              let phoneNumber = vCard.phoneNumbers.first?.value else { return }
         DispatchQueue.main.async { [weak self] in
             guard let self = self else { return }
             VCardUtils.sendVCard(card: vCard,
                                  callID: callID,
                                  accountID: accountID,
-                                 sender: self)
+                                 sender: self, from: phoneNumber.stringValue)
         }
     }
 
@@ -460,9 +497,10 @@ class CallsService: CallsAdapterDelegate {
         guard let call = self.call(callID: callID) else { return }
         let messageDictionary = ["text/plain": message]
         self.callsAdapter.sendTextMessage(withCallID: callID,
-                                          message: messageDictionary,
                                           accountId: accountId.id,
-                                          sMixed: true)
+                                          message: messageDictionary,
+                                          from: call.paricipantHash(),
+                                          isMixed: true)
         let accountHelper = AccountModelHelper(withAccount: accountId)
         let type = accountHelper.isAccountSip() ? URIType.sip : URIType.ring
         let contactUri = JamiURI.init(schema: type, infoHach: call.participantUri, account: accountId)
@@ -480,18 +518,19 @@ class CallsService: CallsAdapterDelegate {
         }
     }
 
-    func sendChunk(callID: String, message: [String: String], accountId: String) {
+    func sendChunk(callID: String, message: [String: String], accountId: String, from: String) {
         self.callsAdapter.sendTextMessage(withCallID: callID,
-                                          message: message,
                                           accountId: accountId,
-                                          sMixed: true)
+                                          message: message,
+                                          from: from,
+                                          isMixed: true)
     }
 
     // MARK: CallsAdapterDelegate
     // swiftlint:disable cyclomatic_complexity
-    func didChangeCallState(withCallId callId: String, state: String, stateCode: NSInteger) {
+    func didChangeCallState(withCallId callId: String, state: String, accountId: String, stateCode: NSInteger) {
 
-        if let callDictionary = self.callsAdapter.callDetails(withCallId: callId) {
+        if let callDictionary = self.callsAdapter.callDetails(withCallId: callId, accountId: accountId) {
             // Add or update new call
             var call = self.calls.value[callId]
             call?.state = CallState(rawValue: state) ?? CallState.unknown
@@ -551,9 +590,9 @@ class CallsService: CallsAdapterDelegate {
                     if let pendingCall = self.call(callID: confId) {
                         DispatchQueue.main.asyncAfter(deadline: .now() + seconds) {
                             if pendingCall.participantsCallId.count == 1 {
-                                self.callsAdapter.joinCall(confId, second: callId)
+                                self.callsAdapter.joinCall(confId, second: callId, accountId: pendingCall.accountId, account2Id: accountId)
                             } else {
-                                self.callsAdapter.joinConference(confId, call: callId)
+                                self.callsAdapter.joinConference(confId, call: callId, accountId: pendingCall.accountId, account2Id: accountId)
                             }
                         }
                     }
@@ -567,7 +606,7 @@ class CallsService: CallsAdapterDelegate {
 
     func didChangeMediaNegotiationStatus(withCallId callId: String, event: String, withMedia: [[String: String]]) {
         guard let call = self.calls.value[callId],
-              let callDictionary = self.callsAdapter.callDetails(withCallId: callId) else { return }
+              let callDictionary = self.callsAdapter.callDetails(withCallId: callId, accountId: call.accountId) else { return }
         call.update(withDictionary: callDictionary, withMedia: withMedia)
         self.currentCallsEvents.onNext(call)
     }
@@ -595,7 +634,7 @@ class CallsService: CallsAdapterDelegate {
                 answerMedias.append(answerMedia)
             }
         }
-        self.callsAdapter.answerMediaChangeResquest(callId, withMedia: answerMedias)
+        self.callsAdapter.answerMediaChangeResquest(callId, accountId: accountId, withMedia: answerMedias)
     }
 
     func shouldCallBeAddedToConference(callId: String) -> String? {
@@ -634,7 +673,7 @@ class CallsService: CallsAdapterDelegate {
     // swiftlint:enable cyclomatic_complexity
 
     func receivingCall(withAccountId accountId: String, callId: String, fromURI uri: String, withMedia mediaList: [[String: String]]) {
-        if let callDictionary = self.callsAdapter.callDetails(withCallId: callId) {
+        if let callDictionary = self.callsAdapter.callDetails(withCallId: callId, accountId: accountId) {
 
             if !isCurrentCall() {
                 var call = self.calls.value[callId]
@@ -692,9 +731,15 @@ class CallsService: CallsAdapterDelegate {
         self.currentCallsEvents.onNext(call)
     }
 
-    func conferenceCreated(conference conferenceID: String) {
+    func conferenceCreated(conference conferenceID: String, accountId: String) {
         let conferenceCalls = Set(self.callsAdapter
-            .getConferenceCalls(conferenceID))
+                                    .getConferenceCalls(conferenceID, accountId: accountId))
+        if conferenceCalls.isEmpty {
+            // no calls attached to a conference. Wait until conference changed to check the calls.
+            createdConferences.insert(conferenceID)
+            return
+        }
+        createdConferences.remove(conferenceID)
         self.pendingConferences.forEach { pending in
             if !conferenceCalls.contains(pending.key) ||
                 conferenceCalls.isDisjoint(with: pending.value) {
@@ -714,7 +759,7 @@ class CallsService: CallsAdapterDelegate {
             values.forEach { (call) in
                 self.call(callID: call)?.participantsCallId = conferenceCalls
             }
-            guard var callDetails = self.callsAdapter.getConferenceDetails(conferenceID) else { return }
+            guard var callDetails = self.callsAdapter.getConferenceDetails(conferenceID, accountId: accountId) else { return }
             callDetails[CallDetailKey.accountIdKey.rawValue] = self.call(callID: callId)?.accountId
             callDetails[CallDetailKey.audioOnlyKey.rawValue] = self.call(callID: callId)?.isAudioOnly.toString()
             let mediaList = [[String: String]]()
@@ -727,10 +772,15 @@ class CallsService: CallsAdapterDelegate {
         }
     }
 
-    func conferenceChanged(conference conferenceID: String, state: String) {
+    func conferenceChanged(conference conferenceID: String, accountId: String, state: String) {
+        if createdConferences.contains(conferenceID) {
+            // a conference was created but calls was not attached to a conference. In this case a conference should be added first.
+            self.conferenceCreated(conference: conferenceID, accountId: accountId)
+            return
+        }
         guard let conference = self.call(callID: conferenceID) else { return }
         let conferenceCalls = Set(self.callsAdapter
-            .getConferenceCalls(conferenceID))
+                                    .getConferenceCalls(conferenceID, accountId: conference.accountId))
         conference.participantsCallId = conferenceCalls
         conferenceCalls.forEach { (callId) in
             guard let call = self.call(callID: callId) else { return }
@@ -759,7 +809,7 @@ class CallsService: CallsAdapterDelegate {
 
         guard let conferenceID = conferences.first, let conference = call(callID: conferenceID) else { return }
         let conferenceCalls = Set(self.callsAdapter
-                  .getConferenceCalls(conferenceID))
+                                    .getConferenceCalls(conferenceID, accountId: conference.accountId))
         conference.participantsCallId = conferenceCalls
         conferenceCalls.forEach { (callID) in
             self.call(callID: callID)?.participantsCallId = conferenceCalls
@@ -767,14 +817,17 @@ class CallsService: CallsAdapterDelegate {
     }
 
     func muteParticipant(confId: String, participantId: String, active: Bool) {
-        self.callsAdapter.muteConferenceParticipant(participantId, forConference: confId, active: active)
+        guard let conference = call(callID: confId) else { return }
+        self.callsAdapter.muteConferenceParticipant(participantId, forConference: confId, accountId: conference.accountId, active: active)
     }
 
     func setModeratorParticipant(confId: String, participantId: String, active: Bool) {
-        self.callsAdapter.setConferenceModerator(participantId, forConference: confId, active: active)
+        guard let conference = call(callID: confId) else { return }
+        self.callsAdapter.setConferenceModerator(participantId, forConference: confId, accountId: conference.accountId, active: active)
     }
 
     func hangupParticipant(confId: String, participantId: String) {
-        self.callsAdapter.hangupConferenceParticipant(participantId, forConference: confId)
+        guard let conference = call(callID: confId) else { return }
+        self.callsAdapter.hangupConferenceParticipant(participantId, forConference: confId, accountId: conference.accountId)
     }
 }
diff --git a/Ring/Ring/Services/ConversationsManager.swift b/Ring/Ring/Services/ConversationsManager.swift
index 112f43a2f4877b9ac1002487e2e50cf146b9a291..23a152c821d72aeeb6136c1b8e89c2f4cd722890 100644
--- a/Ring/Ring/Services/ConversationsManager.swift
+++ b/Ring/Ring/Services/ConversationsManager.swift
@@ -36,7 +36,6 @@ class ConversationsManager {
     private let callService: CallsService
     private let locationSharingService: LocationSharingService
     private let callsProvider: CallsProviderDelegate
-    private let videoService: VideoService
     private let requestService: RequestsService
 
     private let disposeBag = DisposeBag()
@@ -54,7 +53,6 @@ class ConversationsManager {
          locationSharingService: LocationSharingService,
          contactsService: ContactsService,
          callsProvider: CallsProviderDelegate,
-         videoService: VideoService,
          requestsService: RequestsService) {
         self.conversationService = conversationService
         self.accountsService = accountsService
@@ -64,7 +62,6 @@ class ConversationsManager {
         self.locationSharingService = locationSharingService
         self.contactsService = contactsService
         self.callsProvider = callsProvider
-        self.videoService = videoService
         self.requestService = requestsService
 
         ConversationsAdapter.messagesDelegate = self
@@ -161,26 +158,6 @@ class ConversationsManager {
     }
 
     private func subscribeCallsEvents() {
-        self.callService.sharedResponseStream
-            .filter { event in
-                event.eventType == .callEnded
-            }
-            .subscribe { [weak self] event in
-                guard let self = self else { return }
-                guard let accountID: String = event.getEventInput(.accountId) else {
-                    return
-                }
-                guard let jamiId: String = event.getEventInput(.uri) else {
-                    return
-                }
-                guard let call = self.callService.call(participantHash: jamiId.filterOutHost(), accountID: accountID) else { return }
-                self.callsProvider.stopCall(callUUID: call.callUUID)
-                self.videoService.stopCapture()
-                self.videoService.setCameraOrientation(orientation: UIDevice.current.orientation)
-            } onError: { _ in
-            }
-            .disposed(by: disposeBag)
-
         self.callService.newMessage
             .filter({ (event) in
                 return  event.eventType == ServiceEventType.newIncomingMessage
diff --git a/Ring/Ring/Services/DecodingAdapterDelegate.swift b/Ring/Ring/Services/DecodingAdapterDelegate.swift
new file mode 100644
index 0000000000000000000000000000000000000000..d68f5988b3c9d0780a615de68150e1c8a53f029a
--- /dev/null
+++ b/Ring/Ring/Services/DecodingAdapterDelegate.swift
@@ -0,0 +1,26 @@
+/*
+ *  Copyright (C) 2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Kateryna Kostiuk <kateryna.kostiuk@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.
+ */
+
+@objc protocol DecodingAdapterDelegate {
+    func decodingStarted(withRendererId rendererId: String,
+                         withWidth width: Int,
+                         withHeight height: Int)
+    func decodingStopped(withRendererId rendererId: String)
+}
diff --git a/Ring/Ring/Services/VideoAdapterDelegate.swift b/Ring/Ring/Services/VideoAdapterDelegate.swift
index 9f67779f71142a5be708f51b75c401006c1157cd..acccb66de32d3578329c9964da864e0d5be08889 100644
--- a/Ring/Ring/Services/VideoAdapterDelegate.swift
+++ b/Ring/Ring/Services/VideoAdapterDelegate.swift
@@ -19,15 +19,9 @@
  */
 
 @objc protocol VideoAdapterDelegate {
-    func decodingStarted(withRendererId rendererId: String,
-                         withWidth width: Int,
-                         withHeight height: Int,
-                         withCodec codecId: String)
-    func decodingStopped(withRendererId rendererId: String)
     func startCapture(withDevice device: String)
     func stopCapture()
     func writeFrame(withImage image: UIImage?, forCallId: String)
     func setDecodingAccelerated(withState state: Bool)
-    func switchInput(toDevice device: String, callID: String?)
     func fileOpened(for playerId: String, fileInfo: [String: String])
 }
diff --git a/Ring/Ring/Services/VideoManager.swift b/Ring/Ring/Services/VideoManager.swift
new file mode 100644
index 0000000000000000000000000000000000000000..5dc4a9b4bcb9d91f0cfc139cb1eb1621cee141fb
--- /dev/null
+++ b/Ring/Ring/Services/VideoManager.swift
@@ -0,0 +1,83 @@
+/*
+ *  Copyright (C) 2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Kateryna Kostiuk <kateryna.kostiuk@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.
+ */
+
+import RxSwift
+import SwiftyBeaver
+
+class VideoManager {
+
+    let log = SwiftyBeaver.self
+    private let callService: CallsService
+    private let callsProvider: CallsProviderDelegate
+    private let videoService: VideoService
+
+    private let disposeBag = DisposeBag()
+
+    init(with callService: CallsService,
+         callsProvider: CallsProviderDelegate,
+         videoService: VideoService) {
+        self.callService = callService
+        self.callsProvider = callsProvider
+        self.videoService = videoService
+        self.subscribeCallsEvents()
+        VideoAdapter.decodingDelegate = self
+    }
+
+    private func subscribeCallsEvents() {
+        self.callService.sharedResponseStream
+            .filter { event in
+                event.eventType == .callEnded
+            }
+            .subscribe { [weak self] event in
+                guard let self = self else { return }
+                guard let accountID: String = event.getEventInput(.accountId) else {
+                    return
+                }
+                guard let jamiId: String = event.getEventInput(.uri) else {
+                    return
+                }
+                guard let call = self.callService.call(participantHash: jamiId.filterOutHost(), accountID: accountID) else { return }
+                self.callsProvider.stopCall(callUUID: call.callUUID)
+                self.videoService.stopCapture()
+                self.videoService.setCameraOrientation(orientation: UIDevice.current.orientation)
+            } onError: {_ in
+            }
+            .disposed(by: disposeBag)
+    }
+}
+
+extension VideoManager: DecodingAdapterDelegate {
+    func decodingStarted(withRendererId rendererId: String,
+                         withWidth width: Int,
+                         withHeight height: Int) {
+        var accountId = ""
+        var codecId: String?
+        if let call = self.callService.call(callID: rendererId),
+           let codec = self.callService.getVideoCodec(call: call) {
+            codecId = codec
+            accountId = call.accountId
+        }
+        self.videoService.decodingStarted(withRendererId: rendererId, withWidth: width, withHeight: height, withCodec: codecId, withaAccountId: accountId)
+    }
+    func decodingStopped(withRendererId rendererId: String) {
+        self.videoService.decodingStopped(withRendererId: rendererId)
+    }
+
+}
diff --git a/Ring/Ring/Services/VideoService.swift b/Ring/Ring/Services/VideoService.swift
index a681165dd80c2406a358e9cefcebdb328404b60e..b133365ef844377e59cb303b52263ada2254456f 100644
--- a/Ring/Ring/Services/VideoService.swift
+++ b/Ring/Ring/Services/VideoService.swift
@@ -286,18 +286,20 @@ class VideoService: FrameExtractorDelegate {
     private var hardwareAccelerationEnabledByUser = true
     var angle: Int = 0
     var switchInputRequested: Bool = false
+    var currentDeviceId = ""
 
     private let disposeBag = DisposeBag()
 
     init(withVideoAdapter videoAdapter: VideoAdapter) {
         self.videoAdapter = videoAdapter
         currentOrientation = camera.getOrientation
-        VideoAdapter.delegate = self
+        VideoAdapter.videoDelegate = self
         self.hardwareAccelerationEnabledByUser = videoAdapter.getEncodingAccelerated()
         camera.delegate = self
         NotificationCenter.default.addObserver(self, selector: #selector(self.restoreDefaultDevice),
                                                name: NSNotification.Name(rawValue: NotificationName.restoreDefaultVideoDevice.rawValue),
                                                object: nil)
+        self.currentDeviceId = self.videoAdapter.getDefaultDevice()
     }
 
     @objc
@@ -388,12 +390,14 @@ class VideoService: FrameExtractorDelegate {
 }
 
 extension VideoService: VideoAdapterDelegate {
-    func switchInput(toDevice device: String, callID: String?) {
+    func switchInput(toDevice device: String, callID: String?, accountId: String) {
         if let call = callID {
-            videoAdapter.switchInput(device, forCall: call)
+            videoAdapter.switchInput(device, accountId: accountId, forCall: call)
             return
         }
-        videoAdapter.switchInput(device)
+        let current = self.videoAdapter.getDefaultDevice()
+        self.videoAdapter.closeVideoInput(current)
+        self.videoAdapter.openVideoInput(device)
     }
 
     func setDecodingAccelerated(withState state: Bool) {
@@ -423,19 +427,20 @@ extension VideoService: VideoAdapterDelegate {
         }
     }
 
-    func decodingStarted(withRendererId rendererId: String, withWidth width: Int, withHeight height: Int, withCodec codecId: String) {
-        if !codecId.isEmpty {
+    func decodingStarted(withRendererId rendererId: String, withWidth width: Int, withHeight height: Int, withCodec codec: String?, withaAccountId accountId: String) {
+        if let codecId = codec, !codecId.isEmpty {
             // we do not support hardware acceleration with VP8 codec. In this case software
             // encoding will be used. Downgrate resolution if needed. After call finished
             // resolution will be restored in restoreDefaultDevice()
             let codec = VideoCodecs(rawValue: codecId) ?? VideoCodecs.unknown
             if !supportHardware(codec: codec) && self.camera.quality == AVCaptureSession.Preset.hd1280x720 {
                 self.videoAdapter.setDefaultDevice(camera.namePortrait)
-                self.videoAdapter.switchInput("camera://" + camera.namePortrait, forCall: rendererId)
+                self.videoAdapter.switchInput("camera://" + camera.namePortrait, accountId: accountId, forCall: rendererId)
             }
         }
         self.log.debug("Decoding started...")
         videoAdapter.registerSinkTarget(withSinkId: rendererId, withWidth: width, withHeight: height)
+        self.currentDeviceId = self.videoAdapter.getDefaultDevice()
     }
 
     func supportHardware(codec: VideoCodecs) -> Bool {
@@ -464,15 +469,15 @@ extension VideoService: VideoAdapterDelegate {
         self.camera.startCapturing()
     }
 
-    func startCamera() {
-        self.videoAdapter.startCamera()
+    func startMediumCamera() {
+        self.videoAdapter.openVideoInput("camera://" + self.camera.namePortrait)
     }
 
     func videRecordingFinished() {
         if self.cameraPosition == .back {
             self.switchCamera()
         }
-        self.videoAdapter.stopCamera()
+        self.videoAdapter.closeVideoInput("camera://" + self.camera.namePortrait)
         self.stopAudioDevice()
     }
 
@@ -513,7 +518,7 @@ extension VideoService: VideoAdapterDelegate {
                                 orientation: self.getImageOrienation()))
         }
         videoAdapter.writeOutgoingFrame(with: imageBuffer,
-                                        angle: Int32(self.angle))
+                                        angle: Int32(self.angle), videoInputId: "camera://" + self.currentDeviceId)
     }
 
     func updateDevicePosition(position: AVCaptureDevice.Position) {
@@ -526,7 +531,9 @@ extension VideoService: VideoAdapterDelegate {
     }
 
     func startLocalRecorder(audioOnly: Bool, path: String) -> String? {
-        return self.videoAdapter.startLocalRecording(path, audioOnly: audioOnly)
+        let device = audioOnly ? "" : "camera://" + camera.namePortrait
+        self.currentDeviceId = camera.namePortrait
+        return self.videoAdapter.startLocalRecording(device, path: path)
     }
 
     func stopLocalRecorder(path: String) {