diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c
index dd4cd5470ff7837ba39be109d06682a580bfc896..51e404140560a234b2dd1e81bc5b878862f9aed1 100644
--- a/coreapi/callbacks.c
+++ b/coreapi/callbacks.c
@@ -1144,6 +1144,8 @@ static void on_publish_response(SalOp *op) {
 	const SalErrorInfo *ei = op->getErrorInfo();
 
 	if (lev == NULL) return;
+	LinphoneCore *lc = static_cast<LinphoneCore *>(op->getSal()->getUserPointer());
+	if (linphone_core_get_global_state(lc) != LinphoneGlobalOn) return;
 	if (ei->reason == SalReasonNone) {
 		if (linphone_event_get_publish_state(lev) != LinphonePublishTerminating) {
 			SalPublishOp *publishOp = static_cast<SalPublishOp *>(op);
diff --git a/src/conference/participant-device.cpp b/src/conference/participant-device.cpp
index 7dc17a40b6f708edc7341603d39530fa7b9dab6f..5b903424e8039540b068d6a8f22a7f4343e70b7b 100644
--- a/src/conference/participant-device.cpp
+++ b/src/conference/participant-device.cpp
@@ -220,7 +220,6 @@ bool ParticipantDevice::setThumbnailStreamSsrc(uint32_t newSsrc) {
 			lInfo() << "Setting thumbnail stream ssrc of " << *this << " to " << newSsrc;
 		}
 	}
-
 	return changed;
 }
 
@@ -606,8 +605,8 @@ std::set<LinphoneStreamType> ParticipantDevice::updateMediaCapabilities() {
 					audioDir = getStreamDirectionFromSession(LinphoneStreamTypeAudio);
 					videoDir = getStreamDirectionFromSession(LinphoneStreamTypeVideo);
 					textDir = getStreamDirectionFromSession(LinphoneStreamTypeText);
-					thumbnailDir =
-					    mMediaSession->getDirectionOfStream(MediaSessionPrivate::ThumbnailVideoContentAttribute);
+					thumbnailDir = mMediaSession->getDirectionOfStream(
+					    MediaSessionPrivate::ThumbnailVideoContentAttribute, getThumbnailStreamLabel());
 				}
 
 				updateSsrc = isInConference;
@@ -649,7 +648,7 @@ std::set<LinphoneStreamType> ParticipantDevice::updateMediaCapabilities() {
 			mediaCapabilityChanged.insert(LinphoneStreamTypeVideo);
 		}
 
-		bool updateThumbnailSsrc = ((resultThumbnailDir != LinphoneMediaDirectionInactive) || (thumbnailSsrc == 0));
+		bool updateThumbnailSsrc = (resultThumbnailDir != LinphoneMediaDirectionInactive);
 		if (setThumbnailStreamSsrc(thumbnailSsrc) && updateThumbnailSsrc) {
 			mediaCapabilityChanged.insert(LinphoneStreamTypeVideo);
 		}
diff --git a/src/conference/session/media-session.cpp b/src/conference/session/media-session.cpp
index 6c20061c6f73464990efa7fb3f026f508e609eb8..e0c7f8025c61237e1cb58b3e7ec1eb5c7a3dff63 100644
--- a/src/conference/session/media-session.cpp
+++ b/src/conference/session/media-session.cpp
@@ -1791,31 +1791,36 @@ void MediaSessionPrivate::fillConferenceParticipantStream(SalStreamDescription &
 					}
 				}
 			}
-			if (dir == SalStreamInactive) {
-				lWarning() << *q << "Setting " << std::string(sal_stream_type_to_string(type))
-				           << " stream of participant device " << dev->getAddress() << " to inactive (label " << label
-				           << " and content " << content
-				           << ") because he or she doesn't have the send component in its stream capabilities";
+			bool isInactive = (dir == SalStreamInactive);
+			if (isInactive) {
+				lWarning() << *q << ": Setting " << std::string(sal_stream_type_to_string(type)) << " stream of "
+				           << *dev << " to inactive (label " << label << " and content " << content
+				           << ") because the send component is disabled in its stream capabilities";
 			}
 			cfg.dir = dir;
 			if (isVideoStream) {
 				validateVideoStreamDirection(cfg);
 			}
-			if (getParams()->rtpBundleEnabled() && (dir != SalStreamInactive))
-				addStreamToBundle(md, newStream, cfg, mid);
+			if (!isInactive) {
+				if (getParams()->rtpBundleEnabled()) {
+					addStreamToBundle(md, newStream, cfg, mid);
+				}
+				newStream.setSupportedEncryptions(encs);
+			}
 			cfg.replacePayloads(l);
 			newStream.addActualConfiguration(cfg);
-			newStream.setSupportedEncryptions(encs);
 			fillRtpParameters(newStream);
+			lInfo() << "Add stream of type " << sal_stream_type_to_string(type) << " for " << *dev << " (label "
+			        << label << " and content " << content << ") on local offer of " << *q;
 			success = true;
 		}
 		PayloadTypeHandler::clearPayloadList(l);
 	}
 
 	if (!success) {
-		lInfo() << "Don't put video stream for device in conference with address "
-		        << (dev ? dev->getAddress()->toString() : "sip:") << " on local offer for CallSession [" << q
-		        << "] because no valid payload has been found or device is not valid (pointer " << dev << ")";
+		lInfo() << "Don't put stream of type " << sal_stream_type_to_string(type) << " of device with address "
+		        << (dev ? dev->getAddress()->toString() : "sip:") << " on local offer of " << *q
+		        << " because no valid payload has been found or device is not valid (pointer " << dev << ")";
 		cfg.dir = SalStreamInactive;
 		newStream.disable();
 		newStream.type = type;
@@ -1872,7 +1877,12 @@ void MediaSessionPrivate::fillLocalStreamDescription(SalStreamDescription &strea
 				cfg.frame_marking_extension_id = RTP_EXTENSION_FRAME_MARKING;
 			}
 		}
-		if (getParams()->rtpBundleEnabled()) addStreamToBundle(md, stream, cfg, mid);
+		bool isInactive = (dir == SalStreamInactive);
+		if (!isInactive) {
+			if (getParams()->rtpBundleEnabled()) {
+				addStreamToBundle(md, stream, cfg, mid);
+			}
+		}
 
 		// Only used for testing
 		if (q->getCore()->getCCore()->goog_remb_enabled) cfg.rtcp_fb.goog_remb_enabled = TRUE;
@@ -1977,21 +1987,16 @@ SalStreamDescription &MediaSessionPrivate::addStreamToMd(std::shared_ptr<SalMedi
 		}
 	}
 
-	if (streamIdx < 0) {
-		if (freeSlot < 0) {
-			md->streams.resize(currentMdSize + 1);
-			return md->streams[currentMdSize];
-		} else {
-			return md->streams[static_cast<size_t>(freeSlot)];
-		}
-	} else {
+	// Stream position preference
+	if (streamIdx >= 0) {
 		const auto &idx = static_cast<decltype(md->streams)::size_type>(streamIdx);
 		try {
 			auto stream = md->streams.at(idx);
-			// If a stream at the index requested in the the function argument has already been allocated and it is
+			// If a stream at the index requested in the function argument has already been allocated and it is
 			// active, then it must be replaced.
 			if ((stream.getDirection() != SalStreamInactive) && oldMd) {
 				const auto &currentStreamLabel = stream.getLabel();
+				std::string oldStreamLabel;
 				bool currentStreamLabelEmpty = currentStreamLabel.empty();
 				const auto oldMdSize = oldMd->streams.size();
 				int idxOldMd = -1;
@@ -2001,7 +2006,7 @@ SalStreamDescription &MediaSessionPrivate::addStreamToMd(std::shared_ptr<SalMedi
 					    (std::find(protectedStreamNumbersOldMd.cbegin(), protectedStreamNumbersOldMd.cend(),
 					               mdStreamIdx) != protectedStreamNumbersOldMd.cend());
 					auto oldStream = oldMd->getStreamAtIdx(static_cast<unsigned int>(mdStreamIdx));
-					const auto &oldStreamLabel = oldStream.getLabel();
+					oldStreamLabel = oldStream.getLabel();
 					bool oldStreamLabelEmpty = oldStreamLabel.empty();
 					// Select index if either the labels match or the new and old stream have no labels and the index is
 					// not the same. In fact it may happen that a faulty core sends an SDP with multiple streams without
@@ -2024,30 +2029,35 @@ SalStreamDescription &MediaSessionPrivate::addStreamToMd(std::shared_ptr<SalMedi
 					}
 				} else {
 					if (idxOldMd == streamIdx) {
-						lFatal() << "Unable to find available free stream description index:\n- Index in previous SDP: "
-						         << idxOldMd << "\n- Guessed index: " << streamIdx;
+						lFatal()
+						    << *q
+						    << ": Unable to find available free stream description index:\n- Index in previous SDP: "
+						    << idxOldMd << " - label: " << oldStreamLabel << "\n- Guessed index: " << streamIdx
+						    << " - label: " << currentStreamLabel;
 					}
 					auto &streamToFill = addStreamToMd(md, idxOldMd, oldMd);
 					streamToFill = stream;
 				}
 			}
+			lInfo() << *q << ": Add or replace stream at index " << idx;
 			return md->streams.at(idx);
 		} catch (std::out_of_range &) {
-			// If a stream at the index requested in the the function argument has not already been allocated, resize
+			// If a stream at the index requested in the function argument has not already been allocated, resize
 			// the vector list
-			lWarning() << "The current media description has only " << currentMdSize
+			lWarning() << *q << ": The current media description has only " << currentMdSize
 			           << " streams and it has been requested to allocate a stream at index " << idx;
 			md->streams.resize(idx + 1);
 			if (oldMd) {
 				const auto oldMdSize = oldMd->streams.size();
-				lWarning() << "Keep the same type as in the previous media description for all newly allocate streams";
+				lWarning()
+				    << *q << ": Keep the same type as in the previous media description for all newly allocate streams";
 				for (decltype(md->streams)::size_type i = currentMdSize; i < idx; i++) {
 					auto &c = md->streams[i];
 					if (i < oldMdSize) {
 						const auto &s = oldMd->streams[i];
 						c.type = s.type;
 					}
-					lWarning() << "Setting " << std::string(sal_stream_type_to_string(c.type))
+					lWarning() << *q << ": Setting " << std::string(sal_stream_type_to_string(c.type))
 					           << " stream inactive at index " << i << " because of std::out_of_range.";
 					c.setDirection(SalStreamInactive);
 				}
@@ -2055,6 +2065,19 @@ SalStreamDescription &MediaSessionPrivate::addStreamToMd(std::shared_ptr<SalMedi
 			return md->streams.at(idx);
 		}
 	}
+
+	// Free slot
+	if (freeSlot >= 0) {
+		// No preferred stream position has been given but a free slot has been found
+		lInfo() << *q << ": Use free slot at index " << freeSlot;
+		return md->streams[static_cast<size_t>(freeSlot)];
+	}
+
+	// No preference has been given regarding the stream position and no free slots are available, therefore extend the
+	// media description by appending a stream at the end
+	md->streams.resize(currentMdSize + 1);
+	lInfo() << *q << ": Extend local media description to allocate stream at index " << currentMdSize;
+	return md->streams[currentMdSize];
 }
 
 void MediaSessionPrivate::addConferenceLocalParticipantStreams(bool add,
@@ -2107,15 +2130,9 @@ void MediaSessionPrivate::addConferenceLocalParticipantStreams(bool add,
 				     (deviceState == ParticipantDevice::State::OnHold));
 				if (addStream) {
 					SalStreamDescription &newStream = addStreamToMd(md, foundStreamIdx, oldMd);
-					SalStreamConfiguration cfg;
-
 					newStream.type = type;
-					newStream.setContent(content);
-
-					if (!deviceLabel.empty()) {
-						newStream.setLabel(deviceLabel);
-					}
 
+					SalStreamConfiguration cfg;
 					cfg.proto = getParams()->getMediaProto();
 
 					bool bundle_enabled = getParams()->rtpBundleEnabled();
@@ -2127,7 +2144,6 @@ void MediaSessionPrivate::addConferenceLocalParticipantStreams(bool add,
 					         : emptyList),
 					    bundle_enabled);
 					if (!l.empty()) {
-						cfg.replacePayloads(l);
 						newStream.name = "Thumbnail " + std::string(sal_stream_type_to_string(type)) + " " +
 						                 participantDevice->getAddress()->toString();
 						const auto mediaDirection =
@@ -2153,15 +2169,24 @@ void MediaSessionPrivate::addConferenceLocalParticipantStreams(bool add,
 							}
 						}
 						cfg.dir = dir;
-						if (type == SalVideo) {
-							validateVideoStreamDirection(cfg);
-
-							if (!isInLocalConference) cfg.frame_marking_extension_id = RTP_EXTENSION_FRAME_MARKING;
-						}
-						if (bundle_enabled) {
-							const std::string bundleNamePrefix((type == SalVideo) ? "vs" : "as");
-							const std::string bundleName(bundleNamePrefix + std::string("Me") + content);
-							addStreamToBundle(md, newStream, cfg, bundleName);
+						if (dir != SalStreamInactive) {
+							cfg.replacePayloads(l);
+							if (type == SalVideo) {
+								validateVideoStreamDirection(cfg);
+								if (!isInLocalConference) {
+									cfg.frame_marking_extension_id = RTP_EXTENSION_FRAME_MARKING;
+								}
+							}
+							newStream.setSupportedEncryptions(encs);
+							newStream.setContent(content);
+							if (!deviceLabel.empty()) {
+								newStream.setLabel(deviceLabel);
+							}
+							if (bundle_enabled) {
+								const std::string bundleNamePrefix((type == SalVideo) ? "vs" : "as");
+								const std::string bundleName(bundleNamePrefix + std::string("Me") + content);
+								addStreamToBundle(md, newStream, cfg, bundleName);
+							}
 						}
 					} else {
 						lInfo() << "Don't put " << std::string(sal_stream_type_to_string(type))
@@ -2171,7 +2196,6 @@ void MediaSessionPrivate::addConferenceLocalParticipantStreams(bool add,
 					}
 					PayloadTypeHandler::clearPayloadList(l);
 					newStream.addActualConfiguration(cfg);
-					newStream.setSupportedEncryptions(encs);
 					fillRtpParameters(newStream);
 				} else {
 					lWarning() << "Do not add thumbnail stream for the local participant "
@@ -2248,8 +2272,7 @@ void MediaSessionPrivate::addConferenceParticipantStreams(std::shared_ptr<SalMed
 								                           : dev->getStreamLabel(sal_stream_type_to_linphone(type));
 								// main stream has the same label as one of the thumbnail streams
 								const auto &foundStreamIdx =
-								    devLabel.empty() ? -1
-								                     : oldMd->findIdxStreamWithContent(participantContent, devLabel);
+								    devLabel.empty() ? -1 : oldMd->findIdxStreamWithLabel(type, devLabel);
 								lInfo() << "MediaSession [" << q << "] (local address " << *q->getLocalAddress()
 								        << " remote address " << *q->getRemoteAddress() << "] in " << *conference
 								        << " is adding a stream of type "
@@ -2339,9 +2362,8 @@ void MediaSessionPrivate::addConferenceParticipantStreams(std::shared_ptr<SalMed
 							newStream.rtp_port = 0;
 							newStream.rtcp_port = 0;
 							newStream.addActualConfiguration(cfg);
-							lWarning() << *q
-							           << ": New stream added as disabled and inactive because no device has been "
-							              "found with label "
+							lWarning() << *q << ": New stream added at index " << idx
+							           << " as disabled and inactive because no device has been found with label "
 							           << participantsAttrValue << " in " << *conference;
 						}
 					}
@@ -2706,9 +2728,9 @@ void MediaSessionPrivate::makeLocalMediaDescription(bool localIsOfferer,
 					videoStreamIdx = gridStreamIdxWithContent;
 				} else if (activeSpeakerStreamIdxWithContent > -1) {
 					videoStreamIdx = activeSpeakerStreamIdxWithContent;
-				} else {
-					videoStreamIdx = refMd->findIdxBestStream(SalVideo);
 				}
+				// If no stream with content has been found, then let's append the stream. There is no way for the SDK
+				// to guess where the main stream should be
 			} else {
 				videoStreamIdx = refMd->findIdxBestStream(SalVideo);
 			}
@@ -2718,8 +2740,7 @@ void MediaSessionPrivate::makeLocalMediaDescription(bool localIsOfferer,
 		fillLocalStreamDescription(videoStream, md, enableVideoStream, "Video", SalVideo, proto, videoDir, videoCodecs,
 		                           "vs",
 		                           getParams()->getPrivate()->getCustomSdpMediaAttributes(LinphoneStreamTypeVideo));
-
-		if (conference) {
+		if (conference && videoStream.enabled()) {
 			if (participantDevice &&
 			    (isInLocalConference || (!isInLocalConference && remoteContactAddress &&
 			                             remoteContactAddress->hasParam(Conference::IsFocusParameter)))) {
@@ -3667,7 +3688,7 @@ LinphoneStatus MediaSessionPrivate::pause() {
 		q->updateContactAddressInOp();
 
 		if (conference) {
-			lInfo() << "Removing participant with session " << q << " (local addres " << *q->getLocalAddress()
+			lInfo() << "Removing participant with session " << q << " (local address " << *q->getLocalAddress()
 			        << " remote address " << *q->getRemoteAddress() << ")  from conference "
 			        << *conference->getConferenceAddress();
 			// Do not preserve conference after removing the participant
@@ -3949,32 +3970,47 @@ void MediaSessionPrivate::updateCurrentParams() const {
 			getCurrentParams()->enableAudio(false);
 		}
 
-		const auto streamIdx = q->getThumbnailStreamIdx(md);
-		const auto &videoStream =
-		    (streamIdx == -1) ? md->findBestStream(SalVideo) : md->getStreamAtIdx(static_cast<unsigned int>(streamIdx));
-		if (videoStream != Utils::getEmptyConstRefObject<SalStreamDescription>()) {
-			getCurrentParams()->getPrivate()->enableImplicitRtcpFb(videoStream.hasImplicitAvpf());
-
-			const auto videoDirection = getDirFromMd(md, SalVideo);
-			getCurrentParams()->setVideoDirection(videoDirection);
+		const auto mainStreamIdx = q->getMainVideoStreamIdx(md);
+		bool mainStreamEnabled = false;
+		if (mainStreamIdx != -1) {
+			const auto &mainVideoStream = md->getStreamAtIdx(static_cast<unsigned int>(mainStreamIdx));
+			mainStreamEnabled = mainVideoStream.enabled();
+			getCurrentParams()->getPrivate()->enableImplicitRtcpFb(mainVideoStream.hasImplicitAvpf());
 
 			if (getCurrentParams()->getVideoDirection() != LinphoneMediaDirectionInactive) {
 				const std::string &rtpAddr =
-				    (videoStream.getRtpAddress().empty() == false) ? videoStream.getRtpAddress() : md->addr;
+				    (mainVideoStream.getRtpAddress().empty() == false) ? mainVideoStream.getRtpAddress() : md->addr;
 				getCurrentParams()->enableVideoMulticast(!!ms_is_multicast(rtpAddr.c_str()));
 			} else {
 				getCurrentParams()->enableVideoMulticast(false);
 			}
-			const auto enable =
-			    (conference) ? (videoDirection != LinphoneMediaDirectionInactive) : videoStream.enabled();
-			getCurrentParams()->enableVideo(enable);
 		} else {
 			getCurrentParams()->getPrivate()->enableImplicitRtcpFb(false);
-			getCurrentParams()->setVideoDirection(LinphoneMediaDirectionInactive);
 			getCurrentParams()->enableVideoMulticast(false);
-			getCurrentParams()->enableVideo(false);
 		}
 
+		const auto videoDirection = getDirFromMd(md, SalVideo);
+		getCurrentParams()->setVideoDirection(videoDirection);
+		getCurrentParams()->enableVideo((conference) ? (videoDirection != LinphoneMediaDirectionInactive)
+		                                             : mainStreamEnabled);
+
+		SalStreamDir thumbnailStreamDirection = SalStreamInactive;
+		if (conference) {
+			const auto thumbnailStreamIdx = q->getThumbnailStreamIdx();
+			if (thumbnailStreamIdx != -1) {
+				const auto &thumbnailVideoStream = md->getStreamAtIdx(static_cast<unsigned int>(thumbnailStreamIdx));
+				thumbnailStreamDirection = thumbnailVideoStream.getDirection();
+			}
+		} else {
+			thumbnailStreamDirection = SalStreamSendRecv;
+		}
+		// The camera is enabled if:
+		// - the thumbnail stream is enabled
+		// - the thumbnail stream's direction is sendrecv or sendonly
+		getCurrentParams()->enableCamera(
+		    ((thumbnailStreamDirection == SalStreamSendRecv) || (thumbnailStreamDirection == SalStreamSendOnly)) &&
+		    getCurrentParams()->videoEnabled());
+
 		const SalStreamDescription &textStream = md->findBestStream(SalText);
 		if (textStream != Utils::getEmptyConstRefObject<SalStreamDescription>()) {
 			// Direction and multicast are not supported for real-time text.
@@ -4029,7 +4065,7 @@ LinphoneStatus MediaSessionPrivate::startAccept() {
 	if (isThisNotCurrentConference || isThisNotCurrentMediaSession) {
 		if ((linphone_core_get_media_resource_mode(q->getCore()->getCCore()) == LinphoneExclusiveMediaResources) &&
 		    linphone_core_preempt_sound_resources(q->getCore()->getCCore()) != 0) {
-			lInfo() << "Delaying call to " << __func__ << " for media session (local addres " << *q->getLocalAddress()
+			lInfo() << "Delaying call to " << __func__ << " for media session (local address " << *q->getLocalAddress()
 			        << " remote address " << *q->getRemoteAddress() << ") in state " << Utils::toString(state)
 			        << " because sound resources cannot be preempted";
 			q->addPendingAction([this] {
@@ -4363,9 +4399,20 @@ ConferenceLayout MediaSession::computeConferenceLayout(const std::shared_ptr<Sal
 			const auto &params = (isInLocalConference) ? d->getRemoteParams() : d->getParams();
 			layout = params->getConferenceVideoLayout();
 		} else {
-			layout = ConferenceLayout::ActiveSpeaker;
-			lDebug() << "Unable to deduce layout from media description " << md
-			         << " - Default it to: " << Utils::toString(layout);
+			const auto &content = MediaSessionPrivate::ThumbnailVideoContentAttribute;
+			const auto direction = (isInLocalConference) ? SalStreamSendOnly : SalStreamRecvOnly;
+			if ((md->nbActiveStreamsOfType(SalVideo) > 0) && (md->findIdxStreamWithContent(content, direction) == -1)) {
+				layout = ConferenceLayout::Grid;
+				lWarning() << "No stream with a main stream content attribute has been found; nonetheless the media "
+				              "description has at least one stream active video stream and none with content "
+				           << content << " and direction " << sal_stream_dir_to_string(direction)
+				           << ". It means that the remote party is likely to be on a " << Utils::toString(layout)
+				           << " layout and is not wishing to send its camera's video stream";
+			} else {
+				layout = ConferenceLayout::ActiveSpeaker;
+				lDebug() << "Unable to deduce layout from media description " << md
+				         << " - Default it to: " << Utils::toString(layout);
+			}
 		}
 	}
 	return layout;
@@ -5183,7 +5230,7 @@ bool MediaSession::cameraEnabled() const {
 	if (iface) {
 		auto vs = iface->getVideoStream();
 		if (vs && video_stream_local_screen_sharing_enabled(vs)) {
-			auto streamIdx = getLocalThumbnailStreamIdx();
+			auto streamIdx = getThumbnailStreamIdx();
 			if (streamIdx >= 0) iface = dynamic_cast<MS2VideoControl *>(d->getStreamsGroup().getStream(streamIdx));
 		}
 	}
@@ -5200,7 +5247,7 @@ void MediaSession::enableCamera(BCTBX_UNUSED(bool value)) {
 	if (iface) {
 		auto vs = iface->getVideoStream();
 		if (vs && video_stream_local_screen_sharing_enabled(vs)) {
-			auto streamIdx = getLocalThumbnailStreamIdx();
+			auto streamIdx = getThumbnailStreamIdx();
 			if (streamIdx >= 0) iface = dynamic_cast<MS2VideoControl *>(d->getStreamsGroup().getStream(streamIdx));
 		}
 	}
@@ -5426,7 +5473,7 @@ void MediaSession::setNativePreviewWindowId(BCTBX_UNUSED(void *id)) {
 	if (iface) {
 		auto vs = iface->getVideoStream();
 		if (vs && video_stream_local_screen_sharing_enabled(vs)) {
-			auto streamIdx = getLocalThumbnailStreamIdx();
+			auto streamIdx = getThumbnailStreamIdx();
 			if (streamIdx < 0) return;
 			auto videostream = dynamic_cast<VideoControlInterface *>(d->getStreamsGroup().getStream(streamIdx));
 			videostream->setNativePreviewWindowId(id);
@@ -5442,7 +5489,7 @@ void *MediaSession::getNativePreviewVideoWindowId() const {
 	if (iface) {
 		auto vs = iface->getVideoStream();
 		if (vs && video_stream_local_screen_sharing_enabled(vs)) {
-			auto streamIdx = getLocalThumbnailStreamIdx();
+			auto streamIdx = getThumbnailStreamIdx();
 			if (streamIdx < 0) return nullptr;
 			auto videostream = dynamic_cast<VideoControlInterface *>(d->getStreamsGroup().getStream(streamIdx));
 			return videostream->getNativePreviewWindowId();
@@ -5459,7 +5506,7 @@ void *MediaSession::createNativePreviewVideoWindowId() const {
 	if (iface) {
 		auto vs = iface->getVideoStream();
 		if (vs && video_stream_local_screen_sharing_enabled(vs)) {
-			auto streamIdx = getLocalThumbnailStreamIdx();
+			auto streamIdx = getThumbnailStreamIdx();
 			if (streamIdx < 0) return nullptr;
 			auto videostream = dynamic_cast<MS2VideoControl *>(d->getStreamsGroup().getStream(streamIdx));
 			return videostream->createNativePreviewWindowId();
@@ -5533,29 +5580,41 @@ const MediaSessionParams *MediaSession::getRemoteParams() const {
 				                                                  audioStream.custom_sdp_attributes);
 			} else params->enableAudio(false);
 
-			const auto streamIdx = getThumbnailStreamIdx(md);
-			const auto &videoStream = (streamIdx == -1) ? md->findBestStream(SalVideo)
-			                                            : md->getStreamAtIdx(static_cast<unsigned int>(streamIdx));
-			if (videoStream != Utils::getEmptyConstRefObject<SalStreamDescription>()) {
-				const auto videoDir = d->getDirFromMd(md, SalVideo);
-				const auto &videoEnabled = videoStream.enabled();
-				params->enableVideo(videoEnabled || (videoDir != LinphoneMediaDirectionInactive));
-				params->setVideoDirection(videoDir);
-				params->setMediaEncryption(videoStream.hasSrtp() ? LinphoneMediaEncryptionSRTP
-				                                                 : LinphoneMediaEncryptionNone);
+			bool mainStreamEnabled = false;
+			const auto mainStreamIdx = getMainVideoStreamIdx(md);
+			SalStreamDir mainStreamDirection = SalStreamInactive;
+			if (mainStreamIdx != -1) {
+				const auto &mainVideoStream = md->getStreamAtIdx(static_cast<unsigned int>(mainStreamIdx));
+				mainStreamEnabled = mainVideoStream.enabled();
+				mainStreamDirection = mainVideoStream.getDirection();
+				params->setMediaEncryption(mainVideoStream.hasSrtp() ? LinphoneMediaEncryptionSRTP
+				                                                     : LinphoneMediaEncryptionNone);
 				params->getPrivate()->setCustomSdpMediaAttributes(LinphoneStreamTypeVideo,
-				                                                  videoStream.custom_sdp_attributes);
-				// The camera is enabled if:
-				// - the thumbnail stream is enabled
-				// - the thumbnail stream's direction is sendrecv or sendonly
-				const auto &thumbnailDirection = videoStream.getDirection();
-				params->enableCamera(
-				    ((thumbnailDirection == SalStreamSendRecv) || (thumbnailDirection == SalStreamSendOnly)) &&
-				    videoEnabled);
+				                                                  mainVideoStream.custom_sdp_attributes);
+			}
+
+			const auto videoDir = d->getDirFromMd(md, SalVideo);
+			params->setVideoDirection(videoDir);
+			params->enableVideo(mainStreamEnabled || (videoDir != LinphoneMediaDirectionInactive));
+
+			const auto conference = getCore()->findConference(getSharedFromThis(), false);
+			SalStreamDir thumbnailStreamDirection = SalStreamInactive;
+			if (conference) {
+				const auto thumbnailStreamIdx = getThumbnailStreamIdx();
+				if (thumbnailStreamIdx != -1) {
+					const auto &thumbnailVideoStream =
+					    md->getStreamAtIdx(static_cast<unsigned int>(thumbnailStreamIdx));
+					thumbnailStreamDirection = thumbnailVideoStream.getDirection();
+				}
 			} else {
-				params->enableVideo(false);
-				params->enableCamera(false);
+				thumbnailStreamDirection = mainStreamDirection;
 			}
+			// The camera is enabled if:
+			// - the thumbnail stream is enabled
+			// - the thumbnail stream's direction is sendrecv or sendonly
+			params->enableCamera(
+			    ((thumbnailStreamDirection == SalStreamSendRecv) || (thumbnailStreamDirection == SalStreamSendOnly)) &&
+			    params->videoEnabled());
 
 			const SalStreamDescription &textStream = md->findBestStream(SalText);
 			if (textStream != Utils::getEmptyConstRefObject<SalStreamDescription>()) {
@@ -5938,92 +5997,68 @@ int MediaSession::getMainVideoStreamIdx(const std::shared_ptr<SalMediaDescriptio
 	// client is more difficult as the NOTIFY message may have not come or been processed. The algorithm below searches
 	// for the label in the main stream and then reuses the label to look for the desired thumbnail stream
 	auto streamIdx = -1;
-	if (md) {
-		const auto conference = getCore()->findConference(getSharedFromThis(), false);
-		if (conference && d->op) {
-			const bool isInLocalConference = d->getParams()->getPrivate()->getInConference();
-			const auto &confLayout = computeConferenceLayout(isInLocalConference ? d->op->getRemoteMediaDescription()
-			                                                                     : d->op->getLocalMediaDescription());
-			const auto isConferenceLayoutActiveSpeaker = (confLayout == ConferenceLayout::ActiveSpeaker);
-			const auto isConferenceLayoutGrid = (confLayout == ConferenceLayout::Grid);
-			const auto &participantDevice = (isInLocalConference)
-			                                    ? conference->findParticipantDevice(getSharedFromThis())
-			                                    : conference->getMe()->findDevice(getSharedFromThis());
-
-			// Try to find a stream with the screen sharing content attribute as it will be for sure the main video
-			// stream. Indeed, screen sharing can be enabled regardless of the conference layout, it is needed to always
-			// make this try.
-			std::string mainStreamAttrValue = MediaSessionPrivate::ScreenSharingContentAttribute;
-			streamIdx = md->findIdxStreamWithContent(mainStreamAttrValue);
-			if (streamIdx == -1) {
-				// If no stream with the screen sharing content is found, then try with regular attributes for each of
-				// the different layouts
-				if (isConferenceLayoutActiveSpeaker) {
-					mainStreamAttrValue = MediaSessionPrivate::ActiveSpeakerVideoContentAttribute;
-				} else if (isConferenceLayoutGrid) {
-					mainStreamAttrValue = MediaSessionPrivate::GridVideoContentAttribute;
-				} else {
-					lError() << "Unable to determine attribute of main video stream of session " << this
-					         << " (local addres " << *getLocalAddress() << " remote address " << *getRemoteAddress()
-					         << ") in conference " << *conference->getConferenceAddress() << ":";
-					lError() << " - grid layout: " << isConferenceLayoutGrid;
-					lError() << " - active speaker layout: " << isConferenceLayoutActiveSpeaker;
-					lError() << " - device is screen sharing: "
-					         << (participantDevice && participantDevice->screenSharingEnabled());
-				}
-				streamIdx = md->findIdxStreamWithContent(mainStreamAttrValue);
-			}
-			if (streamIdx == -1) {
-				// The stream index was not found despite all efforts
-				lDebug() << "Unable to find main video stream of session " << this << " (local addres "
-				         << *getLocalAddress() << " remote address " << *getRemoteAddress() << "):";
-				lDebug() << " - no stream with content \"" << MediaSessionPrivate::ScreenSharingContentAttribute
-				         << "\" is found";
-				lDebug() << " - grid layout: " << isConferenceLayoutGrid;
-				lDebug() << " - active speaker layout: " << isConferenceLayoutActiveSpeaker;
-				lDebug() << " - device is screen sharing: "
+	const auto conference = getCore()->findConference(getSharedFromThis(), false);
+	if (conference && d->op) {
+		const bool isInLocalConference = d->getParams()->getPrivate()->getInConference();
+		const auto &refMd =
+		    isInLocalConference ? d->op->getRemoteMediaDescription() : d->op->getLocalMediaDescription();
+		const auto &confLayout = computeConferenceLayout(refMd);
+		const auto isConferenceLayoutActiveSpeaker = (confLayout == ConferenceLayout::ActiveSpeaker);
+		const auto isConferenceLayoutGrid = (confLayout == ConferenceLayout::Grid);
+		const auto &participantDevice = (isInLocalConference) ? conference->findParticipantDevice(getSharedFromThis())
+		                                                      : conference->getMe()->findDevice(getSharedFromThis());
+
+		// Try to find a stream with the screen sharing content attribute as it will be for sure the main video
+		// stream. Indeed, screen sharing can be enabled regardless of the conference layout, it is needed to always
+		// make this try.
+		std::string mainStreamAttrValue = MediaSessionPrivate::ScreenSharingContentAttribute;
+		streamIdx = refMd->findIdxStreamWithContent(mainStreamAttrValue);
+		if (streamIdx == -1) {
+			// If no stream with the screen sharing content is found, then try with regular attributes for each of
+			// the different layouts
+			if (isConferenceLayoutActiveSpeaker) {
+				mainStreamAttrValue = MediaSessionPrivate::ActiveSpeakerVideoContentAttribute;
+			} else if (isConferenceLayoutGrid) {
+				mainStreamAttrValue = MediaSessionPrivate::GridVideoContentAttribute;
+			} else {
+				lError() << "Unable to determine attribute of main video stream of " << *this << " (local address "
+				         << *getLocalAddress() << " remote address " << *getRemoteAddress() << ") in " << *conference
+				         << ":";
+				lError() << " - grid layout: " << isConferenceLayoutGrid;
+				lError() << " - active speaker layout: " << isConferenceLayoutActiveSpeaker;
+				lError() << " - device is screen sharing: "
 				         << (participantDevice && participantDevice->screenSharingEnabled());
 			}
+			streamIdx = refMd->findIdxStreamWithContent(mainStreamAttrValue);
 		}
 		if (streamIdx == -1) {
-			streamIdx = md->findIdxBestStream(SalVideo);
-		}
+			// The stream index was not found despite all efforts
+			lDebug() << "Unable to find main video stream of " << *this << " (local address " << *getLocalAddress()
+			         << " remote address " << *getRemoteAddress() << ") in " << *conference << ":";
+			lDebug() << " - no stream with content \"" << MediaSessionPrivate::ScreenSharingContentAttribute
+			         << "\" is found";
+			lDebug() << " - grid layout: " << isConferenceLayoutGrid;
+			lDebug() << " - active speaker layout: " << isConferenceLayoutActiveSpeaker;
+			lDebug() << " - device is screen sharing: "
+			         << (participantDevice && participantDevice->screenSharingEnabled());
+		}
+	} else if (md) {
+		streamIdx = md->findIdxBestStream(SalVideo);
 	}
-
 	return streamIdx;
 }
 
-int MediaSession::getLocalThumbnailStreamIdx() const {
+int MediaSession::getThumbnailStreamIdx() const {
 	L_D();
-	return getThumbnailStreamIdx(d->op ? d->op->getLocalMediaDescription() : nullptr);
-}
-
-int MediaSession::getThumbnailStreamIdx(const std::shared_ptr<SalMediaDescription> &md) const {
-	L_D();
-	// In order to set properly the negotiated parameters, we must know if the client is sending video to the
-	// conference, i.e. look at the thumbnail stream direction. In order to do so, we must know the label of the
-	// searched thumbnail stream. The local case is quite straightforward because all labels are known, but for the
-	// client is more difficult as the NOTIFY message may have not come or been processed. The algorithm below searches
-	// for the label in the main stream and then reuses the label to look for the desired thumbnail stream
 	auto streamIdx = -1;
-	if (md) {
-		const auto conference = getCore()->findConference(getSharedFromThis(), false);
-		if (conference) {
-			const auto content = MediaSessionPrivate::ThumbnailVideoContentAttribute;
-			const bool isInLocalConference = d->getParams()->getPrivate()->getInConference();
-			std::string label;
-			if (isInLocalConference) {
-				auto device = conference->findParticipantDevice(getSharedFromThis());
-				if (device) {
-					label = device->getThumbnailStreamLabel();
-				}
-			} else {
-				auto device = conference->getMe()->findDevice(getSharedFromThis());
-				if (device) {
-					label = device->getThumbnailStreamLabel();
-				}
-			}
-			streamIdx = md->findIdxStreamWithContent(content, label);
+	const auto conference = getCore()->findConference(getSharedFromThis(), false);
+	if (conference) {
+		const bool isInLocalConference = d->getParams()->getPrivate()->getInConference();
+		const auto &refMd =
+		    isInLocalConference ? d->op->getRemoteMediaDescription() : d->op->getLocalMediaDescription();
+		if (refMd) {
+			streamIdx =
+			    refMd->findIdxStreamWithContent(MediaSessionPrivate::ThumbnailVideoContentAttribute, SalStreamSendOnly);
 		}
 	}
 	return streamIdx;
@@ -6033,20 +6068,19 @@ void MediaSession::setEkt(const MSEKTParametersSet *ekt_params) const {
 	getStreamsGroup().setEkt(ekt_params);
 }
 
-LinphoneMediaDirection MediaSession::getDirectionOfStream(BCTBX_UNUSED(const std::string content)) const {
-	auto direction = LinphoneMediaDirectionInactive;
-#ifdef VIDEO_ENABLED
+LinphoneMediaDirection MediaSession::getDirectionOfStream(const std::string &content, const std::string &label) const {
 	L_D();
+	auto direction = LinphoneMediaDirectionInactive;
 	// If we are a conference server, we must look at the incoming INVITE as this SDP has the content attribute.
 	// Look at the local description in all the other scenarions (remote conferece on a server or call)
 	const auto &op = d->getOp();
 	if (op) {
 		bool isServer = linphone_core_conference_server_enabled(getCore()->getCCore());
-		std::shared_ptr<SalMediaDescription> offer =
-		    (isServer) ? op->getRemoteMediaDescription() : op->getLocalMediaDescription();
+		const auto &offer = (isServer) ? op->getLocalMediaDescription() : op->getRemoteMediaDescription();
 		if (offer) {
 			// Look for the index of the stream containing the requested attribute in the offered SDP
-			const auto idx = offer->findIdxStreamWithContent(content);
+			const auto idx = label.empty() ? offer->findIdxStreamWithContent(content)
+			                               : offer->findIdxStreamWithContent(content, label);
 			if (idx != -1) {
 				std::shared_ptr<SalMediaDescription> &md = op->getFinalMediaDescription();
 				try {
@@ -6066,7 +6100,6 @@ LinphoneMediaDirection MediaSession::getDirectionOfStream(BCTBX_UNUSED(const std
 			}
 		}
 	}
-#endif
 	return direction;
 }
 
diff --git a/src/conference/session/media-session.h b/src/conference/session/media-session.h
index 9573630291154a02d858e8acffbfd3b3bdc32abe..58b4ae4b559bb1ec72a7ea2e17989bcbdc94c807 100644
--- a/src/conference/session/media-session.h
+++ b/src/conference/session/media-session.h
@@ -187,8 +187,7 @@ public:
 	uint32_t getSsrc(LinphoneStreamType type) const;
 	uint32_t getSsrc(std::string content) const;
 
-	int getLocalThumbnailStreamIdx() const;
-	int getThumbnailStreamIdx(const std::shared_ptr<SalMediaDescription> &md) const;
+	int getThumbnailStreamIdx() const;
 	int getMainVideoStreamIdx(const std::shared_ptr<SalMediaDescription> &md) const;
 
 	/**
@@ -199,7 +198,8 @@ public:
 	void setEkt(const MSEKTParametersSet *ekt_params) const;
 	bool dtmfSendingAllowed() const;
 
-	LinphoneMediaDirection getDirectionOfStream(const std::string content) const;
+	LinphoneMediaDirection getDirectionOfStream(const std::string &content,
+	                                            const std::string &label = std::string()) const;
 	bool isScreenSharingNegotiated() const;
 	const std::shared_ptr<const VideoSourceDescriptor> getVideoSourceDescriptor() const;
 
diff --git a/src/conference/session/video-stream.cpp b/src/conference/session/video-stream.cpp
index 00299e1abc633656c52eb2949790d5b87104f82a..3dc136a6279b937620889224586b5f8e6329f5f0 100644
--- a/src/conference/session/video-stream.cpp
+++ b/src/conference/session/video-stream.cpp
@@ -398,7 +398,7 @@ void MS2VideoStream::render(const OfferAnswerContext &ctx, CallSession::State ta
 	bool localScreenSharingChanged = false, displayModeChanged = false;
 	auto participantDevice = getMediaSession().getParticipantDevice(LinphoneStreamTypeVideo, label);
 	if (!participantDevice) {
-		if (conference) {
+		if (conference && conference->getMe()) {
 			participantDevice = conference->getMe()->findDevice(LinphoneStreamTypeVideo, label, false);
 			// is Me. Q : Me is always local? (multi account)
 			isScreenSharing = (participantDevice && participantDevice->screenSharingEnabled());
@@ -418,7 +418,7 @@ void MS2VideoStream::render(const OfferAnswerContext &ctx, CallSession::State ta
 						}
 					}
 				} else { // Get Thumbnail Stream.
-					int idx = getMediaSession().getLocalThumbnailStreamIdx();
+					int idx = getMediaSession().getThumbnailStreamIdx();
 					if (idx >= 0) auxStream = dynamic_cast<MS2VideoStream *>(getGroup().getStream(idx));
 					localScreenSharingChanged = enableLocalScreenSharing(isScreenSharing);
 				}
@@ -747,7 +747,7 @@ void MS2VideoStream::render(const OfferAnswerContext &ctx, CallSession::State ta
 						link_video_stream_with_itc_sink(mStream);
 						// Current stream is Main, search for the thumbnail to connect with ITC.
 						MS2VideoStream *vs = nullptr;
-						int idx = getMediaSession().getLocalThumbnailStreamIdx();
+						int idx = getMediaSession().getThumbnailStreamIdx();
 						if (idx >= 0) vs = dynamic_cast<MS2VideoStream *>(getGroup().getStream(idx));
 						if (vs) {
 							VideoStream *itcStream = vs->getVideoStream();
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 65e7351463c4a9dd61428d9aa22fb0fac4be71d0..aebb09694d749d233aa3d6b2e00aafbbd388c70e 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -740,7 +740,7 @@ void CorePrivate::updateVideoDevice() {
 			auto vs = i->getVideoStream();
 			if (vs && video_stream_local_screen_sharing_enabled(vs)) {
 				auto &group = ms->getStreamsGroup();
-				int idx = ms->getLocalThumbnailStreamIdx();
+				int idx = ms->getThumbnailStreamIdx();
 				if (idx >= 0) i = dynamic_cast<MS2VideoControl *>(group.getStream(idx));
 			}
 			if (i) i->parametersChanged();
diff --git a/src/event/event-publish.cpp b/src/event/event-publish.cpp
index 89f54493d27c2b65e978511b742d08229bbb167b..641aaea843baa3d168d239d5f10f4b4fdc9204e1 100644
--- a/src/event/event-publish.cpp
+++ b/src/event/event-publish.cpp
@@ -224,7 +224,7 @@ void EventPublish::terminate() {
 	}
 
 	if (mPublishState != LinphonePublishNone) {
-		if (mPublishState == LinphonePublishOk && mExpires != -1) {
+		if (mOp && (mPublishState == LinphonePublishOk) && (mExpires != -1)) {
 			auto op = dynamic_cast<SalPublishOp *>(mOp);
 			op->unpublish();
 		}
@@ -237,7 +237,7 @@ void EventPublish::terminate() {
 
 void EventPublish::startTimeoutHandling() {
 	stopTimeoutHandling();
-	if (mExpires > 0)
+	if (mExpires > 0) {
 		mTimer = getCore()->createTimer(
 		    [this]() {
 			    lInfo() << "Publish event [" << this << "] has expired";
@@ -245,6 +245,7 @@ void EventPublish::startTimeoutHandling() {
 			    return true;
 		    },
 		    static_cast<unsigned int>(mExpires) * 1000, "Publish timer");
+	}
 }
 
 void EventPublish::stopTimeoutHandling() {
diff --git a/src/event/event.cpp b/src/event/event.cpp
index 08a85e38fa56bfa0c0253e07078f30daf221cafb..47638a56e8b4d3754df2daad5788f9720868e16a 100644
--- a/src/event/event.cpp
+++ b/src/event/event.cpp
@@ -151,7 +151,9 @@ std::shared_ptr<Address> Event::getRemoteContact() const {
 	if (!mRemoteContactAddress) {
 		mRemoteContactAddress = Address::create();
 	}
-	mRemoteContactAddress->setImpl(mOp->getRemoteContactAddress());
+	if (mOp) {
+		mRemoteContactAddress->setImpl(mOp->getRemoteContactAddress());
+	}
 	return mRemoteContactAddress;
 }
 
@@ -159,7 +161,9 @@ std::shared_ptr<Address> Event::cacheFrom() const {
 	if (!mFromAddress) {
 		mFromAddress = Address::create();
 	}
-	mFromAddress->setImpl(mOp->getFromAddress());
+	if (mOp) {
+		mFromAddress->setImpl(mOp->getFromAddress());
+	}
 	return mFromAddress;
 }
 
@@ -167,7 +171,9 @@ std::shared_ptr<Address> Event::cacheTo() const {
 	if (!mToAddress) {
 		mToAddress = Address::create();
 	}
-	mToAddress->setImpl(mOp->getToAddress());
+	if (mOp) {
+		mToAddress->setImpl(mOp->getToAddress());
+	}
 	return mToAddress;
 }
 
@@ -175,7 +181,9 @@ std::shared_ptr<Address> Event::cacheRequestAddress() const {
 	if (!mRequestAddress) {
 		mRequestAddress = Address::create();
 	}
-	mRequestAddress->setImpl(mOp->getRequestAddress());
+	if (mOp) {
+		mRequestAddress->setImpl(mOp->getRequestAddress());
+	}
 	return mRequestAddress;
 }
 
diff --git a/src/sal/sal_stream_description.cpp b/src/sal/sal_stream_description.cpp
index 9f4ee3b500a5155cc8ac0a690d7a9584436b1a9c..586dd67dc474de40432cc50c444904258bffb301 100644
--- a/src/sal/sal_stream_description.cpp
+++ b/src/sal/sal_stream_description.cpp
@@ -524,7 +524,6 @@ SalStreamDescription::addAcapsToConfiguration(const SalStreamConfiguration &base
 				cfgList.push_back(cfg);
 				cfg = baseCfg;
 			}
-
 		} else if (enc == LinphoneMediaEncryptionZRTP) {
 			for (const auto &attr : attrs) {
 				const auto &capNameValue = attr.second;
@@ -1217,7 +1216,7 @@ SalStreamDescription::toSdpMediaDescription(const SalMediaDescription *salMediaD
 		bctbx_free(value);
 	}
 
-	if (actualCfg.conference_ssrc) {
+	if ((actualCfg.conference_ssrc != 0) && !actualCfg.rtcp_cname.empty()) {
 		char *ssrc_attribute = ms_strdup_printf("%u cname:%s", actualCfg.conference_ssrc, actualCfg.rtcp_cname.c_str());
 		belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("ssrc", ssrc_attribute));
 		ms_free(ssrc_attribute);
diff --git a/tester/local-conference-tester-functions.cpp b/tester/local-conference-tester-functions.cpp
index 39785a865ebdbe069dc0ab2f01f87caad9dd3195..91bd36c29e70e9abddf57f4302f0351b1a6a5f26 100644
--- a/tester/local-conference-tester-functions.cpp
+++ b/tester/local-conference-tester-functions.cpp
@@ -181,6 +181,50 @@ void check_conference_me(LinphoneConference *conference, bool_t is_admin) {
 	}
 }
 
+void check_delete_focus_conference_info(std::initializer_list<std::reference_wrapper<CoreManager>> coreMgrs,
+                                        std::list<LinphoneCoreManager *> conferenceMgrs,
+                                        LinphoneCoreManager *focus,
+                                        LinphoneAddress *confAddr,
+                                        time_t end_time) {
+	if (end_time > 0) {
+		long focus_cleanup_window = linphone_core_get_conference_cleanup_period(focus->lc);
+		time_t now = ms_time(NULL);
+		time_t time_left = end_time - now;
+		if (focus_cleanup_window > 0) {
+			time_left += focus_cleanup_window;
+			// The conference information is only deleted by the cleanup timer. Hence even if the end time went by, the
+			// conference information might not be deleted
+			CoreManagerAssert(coreMgrs).waitUntil(chrono::seconds(focus_cleanup_window), [] { return false; });
+		}
+
+		for (const auto &mgr : conferenceMgrs) {
+			LinphoneConferenceInfo *info = linphone_core_find_conference_information_from_uri(mgr->lc, confAddr);
+			if ((mgr == focus) && (time_left <= 0) && (focus_cleanup_window > 0)) {
+				BC_ASSERT_PTR_NULL(info);
+			} else {
+				BC_ASSERT_PTR_NOT_NULL(info);
+			}
+			if (info) {
+				linphone_conference_info_unref(info);
+			}
+		}
+
+		if (time_left > 0) {
+			// wait for the conference to end
+			CoreManagerAssert(coreMgrs).waitUntil(chrono::seconds((time_left + 1)), [] { return false; });
+			LinphoneConferenceInfo *focus_info =
+			    linphone_core_find_conference_information_from_uri(focus->lc, confAddr);
+			if (focus_cleanup_window > 0) {
+				BC_ASSERT_PTR_NULL(focus_info);
+			} else {
+				if (BC_ASSERT_PTR_NOT_NULL(focus_info)) {
+					linphone_conference_info_unref(focus_info);
+				}
+			}
+		}
+	}
+}
+
 LinphoneAddress *
 create_conference_on_server(Focus &focus,
                             ClientConference &organizer,
@@ -257,7 +301,7 @@ create_conference_on_server(Focus &focus,
 		BC_ASSERT_PTR_NOT_NULL(conference);
 		if (conference) {
 			bctbx_list_t *participant_addresses = NULL;
-			for (auto &mgr : participants) {
+			for (const auto &mgr : participants) {
 				participant_addresses = bctbx_list_append(participant_addresses, mgr->identity);
 			}
 			LinphoneCallParams *call_params = linphone_core_create_call_params(organizer.getLc(), NULL);
@@ -303,7 +347,7 @@ create_conference_on_server(Focus &focus,
 
 		int call_ok_cnt = 0;
 		// Conference server dials out participants
-		for (auto &mgr : participants) {
+		for (const auto &mgr : participants) {
 			auto old_stats = participant_stats[idx];
 			if (have_common_audio_payload(mgr, focus.getCMgr()) && !previous_calls[mgr]) {
 				BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallIncomingReceived,
@@ -337,7 +381,7 @@ create_conference_on_server(Focus &focus,
 			duration = static_cast<int>((end_time - start_time) / 60); // duration is expected to be set in minutes
 		}
 
-		for (auto &mgr : participants) {
+		for (const auto &mgr : participants) {
 			previous_calls[mgr] = linphone_core_get_call_by_remote_address2(mgr->lc, focus.getCMgr()->identity);
 		}
 
@@ -443,7 +487,7 @@ create_conference_on_server(Focus &focus,
 	}
 
 	idx = 0;
-	for (auto &mgr : participants) {
+	for (const auto &mgr : participants) {
 		if (!is_dialout || !previous_calls[mgr]) {
 			auto old_stats = participant_stats[idx];
 			if (will_send_ics) {
@@ -745,24 +789,34 @@ void does_all_participants_have_matching_ekt(LinphoneCoreManager *focus,
 			if (security_level == LinphoneConferenceSecurityLevelEndToEnd) {
 				auto firstClientConf = dynamic_cast<const LinphonePrivate::ClientConference *>(
 				    Conference::toCpp(linphone_core_search_conference_2(members.begin()->first->lc, confAddr)));
-				shared_ptr<ClientEktManager::EktContext> firstClientEktCtx;
 				BC_ASSERT_PTR_NOT_NULL(firstClientConf);
 				if (firstClientConf) {
-					firstClientEktCtx = firstClientConf->getClientEktManager()->getEktCtx();
-					BC_ASSERT_PTR_NOT_NULL(firstClientEktCtx);
-					for (auto member : members) {
-						auto conf = linphone_core_search_conference_2(member.first->lc, confAddr);
-						BC_ASSERT_PTR_NOT_NULL(conf);
-						if (conf) {
-							auto rcConf =
-							    dynamic_cast<const LinphonePrivate::ClientConference *>(Conference::toCpp(conf));
-							BC_ASSERT_PTR_NOT_NULL(rcConf);
-							if (rcConf) {
-								auto rcEktCtx = rcConf->getClientEktManager()->getEktCtx();
-								BC_ASSERT_PTR_NOT_NULL(rcEktCtx);
-								BC_ASSERT_EQUAL(firstClientEktCtx->getSSpi(), rcEktCtx->getSSpi(), uint16_t, "%d");
-								BC_ASSERT_TRUE(firstClientEktCtx->getCSpi() == rcEktCtx->getCSpi());
-								BC_ASSERT_TRUE(firstClientEktCtx->getEkt() == rcEktCtx->getEkt());
+					const auto &firstClientEktManager = firstClientConf->getClientEktManager();
+					BC_ASSERT_PTR_NOT_NULL(firstClientEktManager);
+					if (firstClientEktManager) {
+						const auto &firstClientEktCtx = firstClientEktManager->getEktCtx();
+						BC_ASSERT_PTR_NOT_NULL(firstClientEktCtx);
+						for (auto member : members) {
+							auto conf = linphone_core_search_conference_2(member.first->lc, confAddr);
+							BC_ASSERT_PTR_NOT_NULL(conf);
+							if (conf) {
+								auto rcConf =
+								    dynamic_cast<const LinphonePrivate::ClientConference *>(Conference::toCpp(conf));
+								BC_ASSERT_PTR_NOT_NULL(rcConf);
+								if (rcConf) {
+									const auto &clientEktManager = rcConf->getClientEktManager();
+									BC_ASSERT_PTR_NOT_NULL(clientEktManager);
+									if (clientEktManager) {
+										const auto &rcEktCtx = clientEktManager->getEktCtx();
+										BC_ASSERT_PTR_NOT_NULL(rcEktCtx);
+										if (rcEktCtx && firstClientEktCtx) {
+											BC_ASSERT_EQUAL(firstClientEktCtx->getSSpi(), rcEktCtx->getSSpi(), uint16_t,
+											                "%d");
+											BC_ASSERT_TRUE(firstClientEktCtx->getCSpi() == rcEktCtx->getCSpi());
+											BC_ASSERT_TRUE(firstClientEktCtx->getEkt() == rcEktCtx->getEkt());
+										}
+									}
+								}
 							}
 						}
 					}
@@ -4087,25 +4141,8 @@ void create_conference_base(time_t start_time,
 			                             liblinphone_tester_sip_timeout));
 		}
 
-		if (end_time > 0) {
-			time_t now = ms_time(NULL);
-			time_t time_left = end_time - now + linphone_core_get_conference_cleanup_period(focus.getLc());
-			if (time_left < 0) {
-				time_left = 0;
-			}
-			for (auto mgr : allConferenceMgrs) {
-				LinphoneConferenceInfo *info = linphone_core_find_conference_information_from_uri(mgr->lc, confAddr);
-				if (BC_ASSERT_PTR_NOT_NULL(info)) {
-					linphone_conference_info_unref(info);
-				}
-			}
-			// wait for the conference to end
-			CoreManagerAssert({focus, marie, pauline, laure, michelle, berthe})
-			    .waitUntil(chrono::seconds((time_left + 1)), [] { return false; });
-			LinphoneConferenceInfo *focus_info =
-			    linphone_core_find_conference_information_from_uri(focus.getLc(), confAddr);
-			BC_ASSERT_PTR_NULL(focus_info);
-		}
+		check_delete_focus_conference_info({focus, marie, pauline, laure, michelle, berthe}, allConferenceMgrs,
+		                                   focus.getCMgr(), confAddr, end_time);
 
 		std::list<LinphoneCoreManager *> allMembers{marie.getCMgr(), pauline.getCMgr()};
 		if (!version_mismatch) allMembers.push_back(laure.getCMgr());
@@ -4682,7 +4719,6 @@ void create_conference_with_screen_sharing_base(time_t start_time,
 			BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallStreamsRunning,
 			                             focus_stat.number_of_LinphoneCallStreamsRunning + 1,
 			                             liblinphone_tester_sip_timeout));
-			BC_ASSERT_TRUE(check_screen_sharing_sdp(focus.getCMgr(), laure.getCMgr(), FALSE));
 		}
 
 		BC_ASSERT_FALSE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_screen_sharing_enabled,
@@ -6129,19 +6165,8 @@ void create_conference_with_screen_sharing_chat_base(time_t start_time,
 		BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateTerminated, 1,
 		                             liblinphone_tester_sip_timeout));
 
-		long focus_cleanup_window = linphone_core_get_conference_cleanup_period(focus.getLc());
-		if (end_time > 0) {
-			time_t now = ms_time(NULL);
-			time_t time_left = end_time - now;
-			if (focus_cleanup_window > 0) {
-				time_left += focus_cleanup_window;
-			}
-			if (time_left > 0) {
-				// wait for the conference to end
-				CoreManagerAssert({focus, marie, pauline, michelle, laure, berthe})
-				    .waitUntil(chrono::seconds((time_left + 1)), [] { return false; });
-			}
-		}
+		check_delete_focus_conference_info({focus, marie, pauline, laure, michelle, berthe}, conferenceMgrs,
+		                                   focus.getCMgr(), confAddr, end_time);
 
 		BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateDeleted, 1,
 		                             liblinphone_tester_sip_timeout));
@@ -8099,35 +8124,8 @@ void create_conference_with_chat_base(LinphoneConferenceSecurityLevel security_l
 		BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateDeleted, 1,
 		                             liblinphone_tester_sip_timeout));
 
-		long focus_cleanup_window = linphone_core_get_conference_cleanup_period(focus.getLc());
-		if (end_time > 0) {
-			time_t now = ms_time(NULL);
-			time_t time_left = end_time - now;
-			if (focus_cleanup_window > 0) {
-				time_left += focus_cleanup_window;
-			}
-			if (time_left > 0) {
-				for (auto mgr : conferenceMgrs) {
-					LinphoneConferenceInfo *info =
-					    linphone_core_find_conference_information_from_uri(mgr->lc, confAddr);
-					if (BC_ASSERT_PTR_NOT_NULL(info)) {
-						linphone_conference_info_unref(info);
-					}
-				}
-				// wait for the conference to end
-				CoreManagerAssert({focus, marie, pauline, michelle, laure, berthe})
-				    .waitUntil(chrono::seconds((time_left + 1)), [] { return false; });
-				LinphoneConferenceInfo *focus_info =
-				    linphone_core_find_conference_information_from_uri(focus.getLc(), confAddr);
-				if (focus_cleanup_window > 0) {
-					BC_ASSERT_PTR_NULL(focus_info);
-				} else {
-					if (BC_ASSERT_PTR_NOT_NULL(focus_info)) {
-						linphone_conference_info_unref(focus_info);
-					}
-				}
-			}
-		}
+		check_delete_focus_conference_info({focus, marie, pauline, michelle, laure, berthe}, conferenceMgrs,
+		                                   focus.getCMgr(), confAddr, end_time);
 
 		if (!!server_restart) {
 			focus_stat = focus.getStats();
@@ -8304,6 +8302,7 @@ void create_conference_with_chat_base(LinphoneConferenceSecurityLevel security_l
 					const LinphoneErrorInfo *error_info = linphone_call_log_get_error_info(call_log);
 					BC_ASSERT_PTR_NOT_NULL(error_info);
 					if (error_info) {
+						long focus_cleanup_window = linphone_core_get_conference_cleanup_period(focus.getLc());
 						LinphoneReason reason =
 						    (focus_cleanup_window <= 0) ? LinphoneReasonForbidden : LinphoneReasonNotFound;
 						BC_ASSERT_EQUAL(linphone_error_info_get_reason(error_info), reason, int, "%d");
@@ -8824,26 +8823,8 @@ void conference_joined_multiple_times(LinphoneConferenceSecurityLevel security_l
 			});
 		}
 
-		if ((focus_cleanup_window > 0) && (end_time > 0)) {
-			time_t now = ms_time(NULL);
-			time_t time_left = end_time - now + focus_cleanup_window;
-			if (time_left < 0) {
-				time_left = 0;
-			}
-			// wait for the conference to end
-			CoreManagerAssert({focus, marie, pauline, michelle, laure, berthe})
-			    .waitUntil(chrono::seconds((time_left + 1)), [] { return false; });
-		}
-
-		LinphoneConferenceInfo *focus_info =
-		    linphone_core_find_conference_information_from_uri(focus.getLc(), confAddr);
-		if (focus_cleanup_window > 0) {
-			BC_ASSERT_PTR_NULL(focus_info);
-		} else {
-			if (BC_ASSERT_PTR_NOT_NULL(focus_info)) {
-				linphone_conference_info_unref(focus_info);
-			}
-		}
+		check_delete_focus_conference_info({focus, marie, pauline, michelle, laure, berthe}, conferenceMgrs,
+		                                   focus.getCMgr(), confAddr, end_time);
 
 		ms_free(conference_address_str);
 		bctbx_list_free_with_data(participants_info, (bctbx_list_free_func)linphone_participant_info_unref);
@@ -13211,15 +13192,8 @@ void create_conference_with_audio_only_participants_base(LinphoneConferenceSecur
 		BC_ASSERT_TRUE(
 		    wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallReleased, total_focus_calls, 40000));
 
-		if (end_time > 0) {
-			time_t now = ms_time(NULL);
-			time_t time_left = end_time - now + linphone_core_get_conference_cleanup_period(focus.getLc());
-			if (time_left > 0) {
-				// wait for the conference to end
-				CoreManagerAssert({focus, marie, pauline, laure, berthe})
-				    .waitUntil(chrono::seconds((time_left + 1)), [] { return false; });
-			}
-		}
+		check_delete_focus_conference_info({focus, marie, pauline, laure, berthe}, conferenceMgrs, focus.getCMgr(),
+		                                   confAddr, end_time);
 
 		for (auto mgr : {focus.getCMgr(), marie.getCMgr(), pauline.getCMgr(), laure.getCMgr(), berthe.getCMgr()}) {
 
@@ -13676,10 +13650,6 @@ void create_simple_conference_dial_out_with_some_calls_declined_base(LinphoneRea
 					LinphoneParticipantDevice *d = (LinphoneParticipantDevice *)bctbx_list_get_data(itd);
 					bool video_available =
 					    !!linphone_participant_device_get_stream_availability(d, LinphoneStreamTypeVideo);
-					//								if (linphone_conference_is_me(conference,
-					// linphone_participant_device_get_address(d))) {
-					// BC_ASSERT_TRUE(video_available ==
-					// video_enabled); 								} else {
 					LinphoneMediaDirection video_direction =
 					    linphone_participant_device_get_stream_capability(d, LinphoneStreamTypeVideo);
 					BC_ASSERT_TRUE(video_available == (((video_direction == LinphoneMediaDirectionSendOnly) ||
diff --git a/tester/local-conference-tester-functions.h b/tester/local-conference-tester-functions.h
index aeae4f68bb0a0ca3271361246f198a6c029eb223..123f1e0937094a446c8eb93ac78ae554de198ea8 100644
--- a/tester/local-conference-tester-functions.h
+++ b/tester/local-conference-tester-functions.h
@@ -409,6 +409,11 @@ void create_one_participant_conference_toggle_video_base(LinphoneConferenceLayou
 void create_conference_with_active_call_base(bool_t is_dialout);
 
 void check_conference_me(LinphoneConference *conference, bool_t is_me);
+void check_delete_focus_conference_info(std::initializer_list<std::reference_wrapper<CoreManager>> coreMgrs,
+                                        std::list<LinphoneCoreManager *> conferenceMgrs,
+                                        LinphoneCoreManager *focus,
+                                        LinphoneAddress *confAddr,
+                                        time_t end_time);
 
 LinphoneAddress *
 create_conference_on_server(Focus &focus,
diff --git a/tester/local-scheduled-conference-tester.cpp b/tester/local-scheduled-conference-tester.cpp
index a18edb287cb8534d13d6565efc45abc0267986fe..ad60bb627b181fef0a3659f0771c3089fbe09bcf 100644
--- a/tester/local-scheduled-conference-tester.cpp
+++ b/tester/local-scheduled-conference-tester.cpp
@@ -666,19 +666,8 @@ static void alone_in_conference_with_chat_exits_enter(void) {
 		BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateTerminated, 1,
 		                             liblinphone_tester_sip_timeout));
 
-		long focus_cleanup_window = linphone_core_get_conference_cleanup_period(focus.getLc());
-		if (end_time > 0) {
-			time_t now = ms_time(NULL);
-			time_t time_left = end_time - now;
-			if (focus_cleanup_window > 0) {
-				time_left += focus_cleanup_window;
-			}
-			if (time_left > 0) {
-				// wait for the conference to end
-				CoreManagerAssert({focus, marie, pauline, michelle, laure, berthe})
-				    .waitUntil(chrono::seconds((time_left + 1)), [] { return false; });
-			}
-		}
+		check_delete_focus_conference_info({focus, marie, pauline, michelle, laure, berthe}, conferenceMgrs,
+		                                   focus.getCMgr(), confAddr, end_time);
 
 		BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateDeleted, 1,
 		                             liblinphone_tester_sip_timeout));
@@ -1049,19 +1038,8 @@ static void conference_with_participants_late_except_one(void) {
 		BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateTerminated, 1,
 		                             liblinphone_tester_sip_timeout));
 
-		long focus_cleanup_window = linphone_core_get_conference_cleanup_period(focus.getLc());
-		if (end_time > 0) {
-			time_t now = ms_time(NULL);
-			time_t time_left = end_time - now;
-			if (focus_cleanup_window > 0) {
-				time_left += focus_cleanup_window;
-			}
-			if (time_left > 0) {
-				// wait for the conference to end
-				CoreManagerAssert({focus, marie, pauline, michelle, laure, berthe})
-				    .waitUntil(chrono::seconds((time_left + 1)), [] { return false; });
-			}
-		}
+		check_delete_focus_conference_info({focus, marie, pauline, michelle, laure, berthe}, conferenceMgrs,
+		                                   focus.getCMgr(), confAddr, end_time);
 
 		BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateDeleted, 1,
 		                             liblinphone_tester_sip_timeout));
@@ -6068,28 +6046,8 @@ static void create_simple_conference_in_sfu_payload_mode(void) {
 			}
 		}
 
-		if (end_time > 0) {
-			LinphoneConferenceInfo *info = linphone_core_find_conference_information_from_uri(focus.getLc(), confAddr);
-			BC_ASSERT_PTR_NOT_NULL(info);
-			if (info) {
-				linphone_conference_info_unref(info);
-				info = NULL;
-			}
-			time_t now = ms_time(NULL);
-			time_t time_left = end_time - now + linphone_core_get_conference_cleanup_period(focus.getLc());
-			if (time_left > 0) {
-				// wait for the conference to end
-				CoreManagerAssert({focus, marie, pauline}).waitUntil(chrono::seconds((time_left + 1)), [] {
-					return false;
-				});
-			}
-			info = linphone_core_find_conference_information_from_uri(focus.getLc(), confAddr);
-			BC_ASSERT_PTR_NULL(info);
-			if (info) {
-				linphone_conference_info_unref(info);
-				info = NULL;
-			}
-		}
+		check_delete_focus_conference_info({focus, marie, pauline}, conferenceMgrs, focus.getCMgr(), confAddr,
+		                                   end_time);
 
 		BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateTerminationPending,
 		                             1, liblinphone_tester_sip_timeout));
diff --git a/tester/local-transferred-conference-tester.cpp b/tester/local-transferred-conference-tester.cpp
index 28779a0abae83651cd0ad2614b9149426647ff05..521123dc5aaac92cb91ac003e24b0f2b5088cb29 100644
--- a/tester/local-transferred-conference-tester.cpp
+++ b/tester/local-transferred-conference-tester.cpp
@@ -427,16 +427,8 @@ void create_transfer_conference_base(time_t start_time,
 			}
 		}
 
-		if (end_time > 0) {
-			time_t now = ms_time(NULL);
-			time_t time_left = end_time - now + linphone_core_get_conference_cleanup_period(focus.getLc());
-			if (time_left > 0) {
-				// wait for the conference to end
-				CoreManagerAssert({focus, marie, pauline}).waitUntil(chrono::seconds((time_left + 1)), [] {
-					return false;
-				});
-			}
-		}
+		check_delete_focus_conference_info({focus, marie, pauline, laure}, conferenceMgrs, focus.getCMgr(), confAddr,
+		                                   end_time);
 
 		BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateTerminationPending,
 		                             1, liblinphone_tester_sip_timeout));
diff --git a/tester/shared_tester_functions.cpp b/tester/shared_tester_functions.cpp
index 610fad1f23042ca09d0b79bf25d2e45e5e147984..9a9fd1bc281c8a84788f00855a20e62c86c0da8a 100644
--- a/tester/shared_tester_functions.cpp
+++ b/tester/shared_tester_functions.cpp
@@ -956,12 +956,13 @@ bool check_conference_ssrc(LinphoneConference *local_conference, LinphoneConfere
 								// The thumbnail video stream can only be sendonly (or recvonly from the server
 								// standpoint) or inactive. Henceherefore a client that can only receive video, will
 								// have to set it to inactive
+								uint32_t stream_ssrc = linphone_participant_device_get_ssrc(device, type);
 								if (media_direction == LinphoneMediaDirectionInactive) {
-									if (linphone_participant_device_get_ssrc(device, type) != 0) {
+									if (stream_ssrc != 0) {
 										ret = false;
 									}
 								} else {
-									if (linphone_participant_device_get_ssrc(device, type) == 0) {
+									if (stream_ssrc == 0) {
 										ret = false;
 									}
 								}
@@ -972,17 +973,14 @@ bool check_conference_ssrc(LinphoneConference *local_conference, LinphoneConfere
 									if (!stream_available) {
 										continue;
 									}
-									uint32_t video_ssrc = linphone_participant_device_get_ssrc(device, type);
-
 									auto cppDevice = ParticipantDevice::toCpp(device)->getSharedFromThis();
 									bool thumbnail_available = cppDevice->getThumbnailStreamAvailability();
 									uint32_t thumbnail_ssrc = cppDevice->getThumbnailStreamSsrc();
-
 									if (thumbnail_available) {
 										if (thumbnail_ssrc == 0) {
 											ret = false;
 										}
-										if (thumbnail_ssrc == video_ssrc) {
+										if (thumbnail_ssrc == stream_ssrc) {
 											ret = false;
 										}
 									}