You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openmeetings.apache.org by so...@apache.org on 2018/07/25 13:03:38 UTC

[openmeetings] branch 4.0.x updated: [OPENMEETINGS-1142] recording should work as expected

This is an automated email from the ASF dual-hosted git repository.

solomax pushed a commit to branch 4.0.x
in repository https://gitbox.apache.org/repos/asf/openmeetings.git


The following commit(s) were added to refs/heads/4.0.x by this push:
     new 86ce95c  [OPENMEETINGS-1142] recording should work as expected
86ce95c is described below

commit 86ce95c31b8357ae5db22ac99d7af405ff37777b
Author: Maxim Solodovnik <so...@gmail.com>
AuthorDate: Wed Jul 25 20:03:28 2018 +0700

    [OPENMEETINGS-1142] recording should work as expected
---
 .../openmeetings/core/converter/BaseConverter.java |  41 ++-
 .../core/converter/ImageConverter.java             |  29 +-
 .../core/converter/InterviewConverter.java         | 345 +++++++++++----------
 .../core/converter/RecordingConverter.java         |  30 +-
 .../openmeetings/core/data/file/FileProcessor.java |   4 +-
 .../openmeetings/core/remote/MobileService.java    |   7 +-
 .../core/remote/ScopeApplicationAdapter.java       |   6 +-
 .../core/service/RecordingService.java             |  11 +-
 .../openmeetings/db/dao/record/RecordingDao.java   |   6 +-
 .../db/dao/record/RecordingMetaDataDao.java        |   4 +-
 .../apache/openmeetings/db/dto/room/CheckDto.java  |   7 -
 .../openmeetings/db/entity/basic/Client.java       |  21 +-
 .../openmeetings/db/entity/basic/IClient.java      |   2 +
 .../openmeetings/db/entity/file/BaseFileItem.java  |  11 +-
 .../db/entity/record/RecordingMetaData.java        |  24 +-
 .../apache/openmeetings/db/entity/room/Room.java   |  12 -
 .../openmeetings/db/entity/room/StreamClient.java  |  19 +-
 .../apache/openmeetings/backup/BackupImport.java   |   2 +-
 .../org/apache/openmeetings/util/OmFileHelper.java |   2 +-
 .../org/apache/openmeetings/util/StoredFile.java   |  10 +-
 .../openmeetings/web/admin/rooms/RoomsPanel.html   |   3 -
 .../openmeetings/web/app/StreamClientManager.java  |   5 +-
 .../web/room/RoomResourceReference.java            |   7 +-
 .../openmeetings/web/room/sidebar/RoomSidebar.java |  21 +-
 .../sidebar/icon/activity/RoomActivityIcon.java    |   3 +-
 .../openmeetings/web/user/rooms/RoomsPanel.java    |   2 +-
 .../web/util/ProfileImageResourceReference.java    |   4 +-
 openmeetings-web/src/main/webapp/WEB-INF/web.xml   |   2 +-
 openmeetings-web/src/main/webapp/images/delete.gif | Bin 351 -> 0 bytes
 .../main/webapp/{default => images}/deleted.odp    | Bin
 .../main/webapp/{default => images}/deleted.pdf    | Bin
 .../main/webapp/{default => images}/deleted.png    | Bin
 .../src/main/webapp/images/interview_webcam.png    | Bin 0 -> 2146 bytes
 .../src/main/webapp/images/interview_webcam.svg    |   7 +
 .../src/main/webapp/images/profile.jpg             | Bin 9551 -> 0 bytes
 .../src/main/webapp/images/profile.png             | Bin 0 -> 17769 bytes
 .../streams/hibernate/default_interview_image.fla  | Bin 20992 -> 0 bytes
 .../streams/hibernate/default_interview_image.jpg  | Bin 1827 -> 0 bytes
 .../streams/hibernate/default_interview_image.png  | Bin 1106 -> 0 bytes
 .../openmeetings/core/file/TestFileProcessor.java  |   4 +-
 .../apache/openmeetings/util/TestStoredFile.java   |   4 +-
 .../openmeetings/webservice/TestRoomService.java   |   2 +-
 42 files changed, 323 insertions(+), 334 deletions(-)

diff --git a/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/BaseConverter.java b/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/BaseConverter.java
index e9af1e4..4d4465a 100644
--- a/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/BaseConverter.java
+++ b/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/BaseConverter.java
@@ -18,11 +18,13 @@
  */
 package org.apache.openmeetings.core.converter;
 
+import static org.apache.commons.io.FileUtils.copyFile;
 import static org.apache.commons.lang3.math.NumberUtils.toInt;
 import static org.apache.openmeetings.core.data.record.listener.async.BaseStreamWriter.TIME_TO_WAIT_FOR_FRAME;
 import static org.apache.openmeetings.util.OmFileHelper.EXTENSION_FLV;
 import static org.apache.openmeetings.util.OmFileHelper.EXTENSION_PNG;
 import static org.apache.openmeetings.util.OmFileHelper.getRecordingMetaData;
+import static org.apache.openmeetings.util.OmFileHelper.getStreamsHibernateDir;
 import static org.apache.openmeetings.util.OmFileHelper.getStreamsSubDir;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_PATH_FFMPEG;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_PATH_IMAGEMAGIC;
@@ -151,7 +153,7 @@ public abstract class BaseConverter {
 		}
 	}
 
-	protected String[] mergeAudioToWaves(List<File> waveFiles, File wav) throws IOException {
+	private String[] mergeAudioToWaves(List<File> waveFiles, File wav) throws IOException {
 		List<String> argv = new ArrayList<>();
 
 		argv.add(getPathToSoX());
@@ -164,11 +166,30 @@ public abstract class BaseConverter {
 		return argv.toArray(new String[0]);
 	}
 
-	protected void stripAudioFirstPass(Recording recording, ProcessResultList logs,
-			List<File> waveFiles, File streamFolder)
-	{
-		stripAudioFirstPass(recording, logs, waveFiles, streamFolder
-				, metaDataDao.getAudioMetaDataByRecording(recording.getId()));
+	private void stripAudioFirstPass(Recording r, ProcessResultList logs, List<File> waveFiles, File streamFolder) {
+		stripAudioFirstPass(r, logs, waveFiles, streamFolder, metaDataDao.getAudioMetaDataByRecording(r.getId()));
+	}
+
+	protected void createWav(Recording r, ProcessResultList logs, File streamFolder, List<File> waveFiles, File wav) throws IOException {
+		deleteFileIfExists(wav);
+		stripAudioFirstPass(r, logs, waveFiles, streamFolder);
+		if (waveFiles.isEmpty()) {
+			// create default Audio to merge it. strip to content length
+			String oneSecWav = new File(getStreamsHibernateDir(), "one_second.wav").getCanonicalPath();
+
+			// Calculate delta at beginning
+			double duration = diffSeconds(r.getRecordEnd(), r.getRecordStart());
+
+			String[] cmd = new String[] { getPathToSoX(), oneSecWav, wav.getCanonicalPath(), "pad", "0", String.valueOf(duration) };
+
+			logs.add(ProcessHelper.executeScript("generateSampleAudio", cmd));
+		} else if (waveFiles.size() == 1) {
+			copyFile(waveFiles.get(0), wav);
+		} else {
+			String[] soxArgs = mergeAudioToWaves(waveFiles, wav);
+
+			logs.add(ProcessHelper.executeScript("mergeAudioToWaves", soxArgs));
+		}
 	}
 
 	private String[] addSoxPad(ProcessResultList logs, String job, double length, double position, File inFile, File outFile) throws IOException {
@@ -253,7 +274,7 @@ public abstract class BaseConverter {
 		return metaData;
 	}
 
-	protected void stripAudioFirstPass(Recording recording,
+	private void stripAudioFirstPass(Recording recording,
 			ProcessResultList logs,
 			List<File> waveFiles, File streamFolder,
 			List<RecordingMetaData> metaDataList) {
@@ -418,17 +439,21 @@ public abstract class BaseConverter {
 		return new Dimension(100, 100); // will return 100x100 for non-video to be able to play
 	}
 
-	protected void postProcess(Recording r, String mp4path, ProcessResultList logs, List<File> waveFiles) throws IOException {
+	protected void finalize(Recording r, String mp4path, ProcessResultList logs) throws IOException {
 		convertToPng(r, mp4path, logs);
 
 		updateDuration(r);
 		r.setStatus(Recording.Status.PROCESSED);
+	}
 
+	protected void postProcess(Recording r, ProcessResultList logs) {
 		logDao.delete(r);
 		for (ProcessResult res : logs.getJobs()) {
 			logDao.add("generateFFMPEG", r, res);
 		}
+	}
 
+	protected void postProcess(List<File> waveFiles) {
 		// Delete Wave Files
 		for (File audio : waveFiles) {
 			if (audio.exists()) {
diff --git a/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/ImageConverter.java b/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/ImageConverter.java
index 0cee85c..d45a1e3 100644
--- a/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/ImageConverter.java
+++ b/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/ImageConverter.java
@@ -20,9 +20,7 @@ package org.apache.openmeetings.core.converter;
 
 import static org.apache.commons.io.FileUtils.copyFile;
 import static org.apache.openmeetings.util.OmFileHelper.DOC_PAGE_PREFIX;
-import static org.apache.openmeetings.util.OmFileHelper.EXTENSION_JPG;
 import static org.apache.openmeetings.util.OmFileHelper.EXTENSION_PNG;
-import static org.apache.openmeetings.util.OmFileHelper.JPG_MIME_TYPE;
 import static org.apache.openmeetings.util.OmFileHelper.PNG_MIME_TYPE;
 import static org.apache.openmeetings.util.OmFileHelper.PROFILE_FILE_NAME;
 import static org.apache.openmeetings.util.OmFileHelper.getUploadProfilesUserDir;
@@ -36,6 +34,7 @@ import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.Date;
 
 import org.apache.commons.io.FileUtils;
 import org.apache.openmeetings.db.dao.user.UserDao;
@@ -71,16 +70,16 @@ public class ImageConverter extends BaseConverter {
 	}
 
 	public ProcessResultList convertImage(BaseFileItem f, StoredFile sf, ProcessResultList logs) throws IOException {
-		File jpg = f.getFile(EXTENSION_JPG);
-		if (!sf.isJpg()) {
+		File png = f.getFile(EXTENSION_PNG);
+		if (!sf.isPng()) {
 			File img = f.getFile(sf.getExt());
 
-			log.debug("##### convertImage destinationFile: " + jpg);
-			logs.add(convertSingleJpg(img, jpg));
-		} else if (!jpg.exists()){
-			copyFile(f.getFile(sf.getExt()), jpg);
+			log.debug("##### convertImage destinationFile: " + png);
+			logs.add(convertSinglePng(img, png));
+		} else if (!png.exists()){
+			copyFile(f.getFile(sf.getExt()), png);
 		}
-		logs.add(initSize(f, jpg, JPG_MIME_TYPE));
+		logs.add(initSize(f, png, PNG_MIME_TYPE));
 		return logs;
 	}
 
@@ -88,16 +87,16 @@ public class ImageConverter extends BaseConverter {
 		ProcessResultList returnMap = new ProcessResultList();
 
 		// User Profile Update
-		File[] files = getUploadProfilesUserDir(userId).listFiles(fi -> fi.getName().endsWith(EXTENSION_JPG));
+		File[] files = getUploadProfilesUserDir(userId).listFiles(fi -> fi.getName().endsWith(EXTENSION_PNG));
 		if (files != null) {
 			for (File f : files) {
 				FileUtils.deleteQuietly(f);
 			}
 		}
 
-		File destinationFile = OmFileHelper.getNewFile(getUploadProfilesUserDir(userId), PROFILE_FILE_NAME, EXTENSION_JPG);
+		File destinationFile = OmFileHelper.getNewFile(getUploadProfilesUserDir(userId), PROFILE_FILE_NAME, EXTENSION_PNG);
 		if (!skipConvertion) {
-			returnMap.add(convertSingleJpg(file, destinationFile));
+			returnMap.add(convertSinglePng(file, destinationFile));
 		} else {
 			FileUtils.copyFile(file, destinationFile);
 		}
@@ -109,7 +108,7 @@ public class ImageConverter extends BaseConverter {
 
 		String pictureuri = destinationFile.getName();
 		User us = userDao.get(userId);
-		us.setUpdated(new java.util.Date());
+		us.setUpdated(new Date());
 		us.setPictureuri(pictureuri);
 		userDao.update(us, userId);
 
@@ -151,10 +150,10 @@ public class ImageConverter extends BaseConverter {
 	 * @throws IOException
 	 *
 	 */
-	private ProcessResult convertSingleJpg(File in, File out) throws IOException {
+	private ProcessResult convertSinglePng(File in, File out) throws IOException {
 		String[] argv = new String[] { getPathToConvert(), in.getCanonicalPath(), out.getCanonicalPath() };
 
-		return ProcessHelper.executeScript("convertSingleJpg", argv);
+		return ProcessHelper.executeScript("convertSinglePng", argv);
 	}
 
 	public ProcessResult resize(File in, File out, Integer width, Integer height) throws IOException {
diff --git a/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/InterviewConverter.java b/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/InterviewConverter.java
index d70ad06..49896c2 100644
--- a/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/InterviewConverter.java
+++ b/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/InterviewConverter.java
@@ -18,16 +18,19 @@
  */
 package org.apache.openmeetings.core.converter;
 
-import static org.apache.openmeetings.util.OmFileHelper.EXTENSION_FLV;
 import static org.apache.openmeetings.util.OmFileHelper.getRecordingMetaData;
-import static org.apache.openmeetings.util.OmFileHelper.getStreamsHibernateDir;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.getWebAppRootKey;
 
 import java.io.File;
-import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
 import java.util.UUID;
+import java.util.stream.Collectors;
 
 import org.apache.openmeetings.db.dao.record.RecordingDao;
 import org.apache.openmeetings.db.dao.record.RecordingMetaDataDao;
@@ -46,14 +49,6 @@ import org.springframework.stereotype.Component;
 @Component
 public class InterviewConverter extends BaseConverter implements IRecordingConverter {
 	private static final Logger log = Red5LoggerFactory.getLogger(InterviewConverter.class, getWebAppRootKey());
-	private static class ReConverterParams {
-		private int leftSideLoud = 1;
-		private int rightSideLoud = 1;
-		@SuppressWarnings("unused")
-		private Integer leftSideTime = 0;
-		@SuppressWarnings("unused")
-		private Integer rightSideTime = 0;
-	}
 
 	// Spring loaded Beans
 	@Autowired
@@ -61,45 +56,18 @@ public class InterviewConverter extends BaseConverter implements IRecordingConve
 	@Autowired
 	private RecordingMetaDataDao metaDataDao;
 
-	private String[] mergeAudioToWaves(List<File> waveFiles, File wav,
-			List<RecordingMetaData> metaDataList, ReConverterParams rcv) throws IOException {
-		String[] cmdSox = new String[waveFiles.size() + 5];
-		cmdSox[0] = this.getPathToSoX();
-		cmdSox[1] = "-m";
-
-		int counter = 2;
-		for (File _wav : waveFiles) {
-			for (RecordingMetaData metaData : metaDataList) {
-				String hashFileFullNameStored = metaData.getFullWavAudioData();
-
-				if (hashFileFullNameStored.equals(_wav.getName())) {
-					if (metaData.getInteriewPodId() == 1) {
-						cmdSox[counter] = "-v " + rcv.leftSideLoud;
-						counter++;
-					}
-					if (metaData.getInteriewPodId() == 2) {
-						cmdSox[counter] = "-v " + rcv.rightSideLoud;
-						counter++;
-					}
-				}
-			}
-			cmdSox[counter] = _wav.getCanonicalPath();
-			counter++;
-		}
-
-		cmdSox[counter] = wav.getCanonicalPath();
-
-		return cmdSox;
-	}
-
 	@Override
-	public void startConversion(Long recordingId) {
-		startConversion(recordingId, false, new ReConverterParams());
-	}
-
-	public void startConversion(Long id, boolean reconversion, ReConverterParams rcv) {
+	public void startConversion(Long id) {
 		Recording r = null;
+		ProcessResultList logs = new ProcessResultList();
+		List<File> waveFiles = new ArrayList<>();
 		try {
+			// Default Image for empty interview video pods
+			final File interviewCamFile = new File(OmFileHelper.getImagesDir(), "interview_webcam.png");
+			if (!interviewCamFile.exists()) {
+				throw new ConversionException("defaultInterviewImageFile does not exist!");
+			}
+
 			r = recordingDao.get(id);
 			log.debug("recording {}", r.getId());
 			if (Strings.isEmpty(r.getHash())) {
@@ -108,109 +76,104 @@ public class InterviewConverter extends BaseConverter implements IRecordingConve
 			r.setStatus(Recording.Status.CONVERTING);
 			r = recordingDao.update(r);
 
-			ProcessResultList logs = new ProcessResultList();
-			List<File> waveFiles = new ArrayList<>();
 			File streamFolder = getStreamFolder(r);
-			List<RecordingMetaData> metaDataList = metaDataDao.getAudioMetaDataByRecording(r.getId());
-
-			stripAudioFirstPass(r, logs, waveFiles, streamFolder, metaDataList);
-
-			// Merge Wave to Full Length
-			File streamFolderGeneral = getStreamsHibernateDir();
+			List<RecordingMetaData> metaList = metaDataDao.getByRecording(r.getId());
 
 			File wav = new File(streamFolder, String.format("INTERVIEW_%s_FINAL_WAVE.wav", r.getId()));
-			deleteFileIfExists(wav);
-
-			if (waveFiles.isEmpty()) {
-				// create default Audio to merge it.
-				// strip to content length
-				File outputWav = new File(streamFolderGeneral, "one_second.wav");
-
-				// Calculate delta at beginning
-				double deltaPadding = diffSeconds(r.getRecordEnd(), r.getRecordStart());
-
-				String[] cmdSox = new String[] { getPathToSoX(), outputWav.getCanonicalPath(), wav.getCanonicalPath(), "pad", "0", String.valueOf(deltaPadding) };
+			createWav(r, logs, streamFolder, waveFiles, wav);
 
-				logs.add(ProcessHelper.executeScript("generateSampleAudio", cmdSox));
-			} else if (waveFiles.size() == 1) {
-				wav = waveFiles.get(0);
-			} else {
-				String[] soxArgs;
-				if (reconversion) {
-					soxArgs = mergeAudioToWaves(waveFiles, wav, metaDataList, rcv);
-				} else {
-					soxArgs = mergeAudioToWaves(waveFiles, wav);
-				}
-
-				logs.add(ProcessHelper.executeScript("mergeAudioToWaves", soxArgs));
-			}
-			// Default Image for empty interview video pods
-			final File defaultInterviewImageFile = new File(streamFolderGeneral, "default_interview_image.png");
-
-			if (!defaultInterviewImageFile.exists()) {
-				throw new ConversionException("defaultInterviewImageFile does not exist!");
-			}
+			final String interviewCam = interviewCamFile.getCanonicalPath();
 
-			final int flvWidth = 320;
-			final int flvHeight = 260;
+			final int width = 320;
+			final int height = 260;
 			// Merge Audio with Video / Calculate resulting FLV
 
-			String[] pods = new String[2];
-			boolean found = false;
-			for (RecordingMetaData meta : metaDataList) {
-				File flv = getRecordingMetaData(r.getRoomId(), meta.getStreamName());
-
-				Integer pod = meta.getInteriewPodId();
-				if (flv.exists() && pod != null && pod > 0 && pod < 3) {
-					String path = flv.getCanonicalPath();
-					/*
-					 * CHECK FILE:
-					 * ffmpeg -i rec_316_stream_567_2013_08_28_11_51_45.flv -v error -f null file.null
+			// group by sid first to get all pods
+			Map<String, List<RecordingMetaData>> metaBySid = metaList.stream().collect(
+					Collectors.groupingBy(RecordingMetaData::getSid
+					, () -> new LinkedHashMap<>()
+					, Collectors.collectingAndThen(Collectors.toList(), l -> l.stream().sorted(Comparator.comparing(RecordingMetaData::getRecordStart)).collect(Collectors.toList()))));
+			List<String> pods = new ArrayList<>();
+			int N = pods.size();
+			for (Entry<String, List<RecordingMetaData>> e : metaBySid.entrySet()) {
+				Date pStart = r.getRecordStart();
+				List<PodPart> parts = new ArrayList<>();
+				for (RecordingMetaData meta : e.getValue()) {
+					File flv = getRecordingMetaData(r.getRoomId(), meta.getStreamName());
+					if (flv.exists()) {
+						String path = flv.getCanonicalPath();
+						/* CHECK FILE:
+						 * ffmpeg -i rec_316_stream_567_2013_08_28_11_51_45.flv -v error -f null file.null
+						 */
+						String[] args = new String[] {getPathToFFMPEG(), "-y"
+								, "-i", path
+								, "-v", "error"
+								, "-f", "null"
+								, "file.null"};
+						ProcessResult res = ProcessHelper.executeScript(String.format("checkFlvPod_%s_%s", N, parts.size()), args, true);
+						logs.add(res);
+						if (!res.isWarn()) {
+							long diff = diff(meta.isAudioOnly() ? meta.getRecordEnd() : meta.getRecordStart(), pStart);
+							//createBlankPod(id, streamFolder, interviewCam, diff, logs, pods, parts);
+							PodPart.add(parts, diff);
+							if (!meta.isAudioOnly()) {
+								parts.add(new PodPart(path));
+							}
+							pStart = meta.getRecordEnd();
+						}
+					}
+				}
+				if (!parts.isEmpty()) {
+					String podX = new File(streamFolder, String.format("rec_%s_pod_%s.flv", id, N)).getCanonicalPath();
+					long diff = diff(pStart, r.getRecordEnd());
+					// add blank pod till the end
+					//createBlankPod(id, streamFolder, interviewCam, diff, logs, pods, parts);
+					PodPart.add(parts, diff);
+					/* create continuous pod
+					 * ffmpeg \
+					 *	-loop 1 -framerate 24 -t 10 -i image1.jpg \
+					 *	-i video.mp4 \
+					 *	-loop 1 -framerate 24 -t 10 -i image2.jpg \
+					 *	-loop 1 -framerate 24 -t 10 -i image3.jpg \
+					 *	-filter_complex "[0][1][2][3]concat=n=4:v=1:a=0" out.mp4
 					 */
-					String[] args = new String[] {getPathToFFMPEG(), "-y"
-							, "-i", path
-							, "-an" // only input files with video will be treated as video sources
-							, "-v", "error"
-							, "-f", "null"
-							, "file.null"};
-					ProcessResult res = ProcessHelper.executeScript("checkFlvPod_" + pod , args, true);
-					logs.add(res);
-					if (res.isOk()) {
-						long diff = diff(meta.getRecordStart(), meta.getRecording().getRecordStart());
-						if (diff != 0L) {
-							// stub to add
-							// ffmpeg -y -loop 1 -i /home/solomax/work/openmeetings/branches/3.0.x/dist/red5/webapps/openmeetings/streams/hibernate/default_interview_image.jpg -filter_complex '[0:0]scale=320:260' -c:v libx264 -t 00:00:29.059 -pix_fmt yuv420p out.flv
-							File podFB = new File(streamFolder, String.format("%s_pod_%s_blank.flv", meta.getStreamName(), pod));
-							String podPB = podFB.getCanonicalPath();
-							String[] argsPodB = new String[] { getPathToFFMPEG(), "-y" //
-									, "-loop", "1", "-i", defaultInterviewImageFile.getCanonicalPath() //
-									, "-filter_complex", String.format("[0:0]scale=%1$d:%2$d", flvWidth, flvHeight) //
-									, "-c:v", "libx264" //
-									, "-t", formatMillis(diff) //
-									, "-pix_fmt", "yuv420p" //
-									, podPB };
-							logs.add(ProcessHelper.executeScript("blankFlvPod_" + pod , argsPodB));
-
-							//ffmpeg -y -i out.flv -i rec_15_stream_4_2014_07_15_20_41_03.flv -filter_complex '[0:0]setsar=1/1[sarfix];[1:0]scale=320:260,setsar=1/1[scale];[sarfix] [scale] concat=n=2:v=1:a=0 [v]' -map '[v]'  output1.flv
-							File podF = new File(streamFolder, OmFileHelper.getName(meta.getStreamName() + "_pod_" + pod, EXTENSION_FLV));
-							String podP = podF.getCanonicalPath();
-							String[] argsPod = new String[] { getPathToFFMPEG(), "-y"//
-									, "-i", podPB //
-									, "-i", path //
-									, "-filter_complex", String.format("[0:0]setsar=1/1[sarfix];[1:0]scale=%1$d:%2$d,setsar=1/1[scale];[sarfix] [scale] concat=n=2:v=1:a=0 [v]", flvWidth, flvHeight) //
-									, "-map", "[v]" //
-									, podP };
-							logs.add(ProcessHelper.executeScript("shiftedFlvPod_" + pod , argsPod));
-
-							pods[pod - 1] = podP;
+					List<String> args = new ArrayList<>();
+					args.add(getPathToFFMPEG());
+					args.add("-y");
+					args.add("-an");
+					StringBuilder videos = new StringBuilder();
+					StringBuilder concat = new StringBuilder();
+					for (int i = 0; i < parts.size(); ++i) {
+						PodPart p = parts.get(i);
+						if (p.getFile() == null) {
+							args.add("-loop");
+							args.add("1");
+							args.add("-t");
+							args.add(formatMillis(p.getDuration()));
+							args.add("-i");
+							args.add(interviewCam);
 						} else {
-							pods[pod - 1] = path;
+							args.add("-i");
+							args.add(p.getFile());
 						}
+						videos.append('[').append(i).append(']')
+							.append("scale=").append(width).append(':').append(height)
+							.append("[v").append(i).append("]; ");
+						concat.append("[v").append(i).append(']');
 					}
-					found = true;
+					args.add("-filter_complex");
+					args.add(concat.insert(0, videos).append("concat=n=").append(parts.size()).append(":v=1:a=0").toString());
+					args.add(podX);
+					ProcessResult res = ProcessHelper.executeScript(String.format("Full Flv pod_%s", N), args.toArray(new String[0]), true);
+					logs.add(res);
+					if (res.isWarn()) {
+						throw new ConversionException("Fail to create pod");
+					}
+					pods.add(podX);
+					N = pods.size();
 				}
 			}
-			if (!found) {
+			if (N == 0) {
 				ProcessResult res = new ProcessResult();
 				res.setProcess("CheckFlvFilesExists");
 				res.setError("No valid pods found");
@@ -218,47 +181,99 @@ public class InterviewConverter extends BaseConverter implements IRecordingConve
 				logs.add(res);
 				return;
 			}
-			boolean shortest = false;
+			double ratio = Math.sqrt(N / Math.sqrt(2));
+			int w = ratio < 1 ? N : (int)Math.round(ratio);
+			w = Math.max(w, (int)Math.round(1. * N / w));
 			List<String> args = new ArrayList<>();
-			for (int i = 0; i < 2; ++i) {
-				/*
-				 * INSERT BLANK INSTEAD OF BAD PAD:
-				 * ffmpeg -loop 1 -i default_interview_image.jpg -i rec_316_stream_569_2013_08_28_11_51_45.flv -filter_complex '[0:v]scale=320:260,pad=2*320:260[left];[1:v]scale=320:260[right];[left][right]overlay=main_w/2:0' -shortest -y out4.flv
-				 *
-				 * JUST MERGE:
-				 * ffmpeg -i rec_316_stream_569_2013_08_28_11_51_45.flv -i rec_316_stream_569_2013_08_28_11_51_45.flv -filter_complex '[0:v]scale=320:260,pad=2*320:260[left];[1:v]scale=320:260[right];[left][right]overlay=main_w/2:0' -y out4.flv
+			if (N == 1) {
+				args.add("-i");
+				args.add(pods.get(0));
+				args.add("-i");
+				args.add(wav.getCanonicalPath());
+				args.add("-map");
+				args.add("[0]");
+			} else {
+				/* Creating grid
+				 * ffmpeg -i top_l.mp4 -i top_r.mp4 -i bottom_l.mp4 -i bottom_r.mp4 -i audio.mp4 \
+				 *	-filter_complex "[0:v][1:v]hstack[t];[2:v][3:v]hstack[b];[t][b]vstack[v]" \
+				 *	-map "[v]" -map 4:a -c:a copy -shortest output.mp4
 				 */
-				if (pods[i] == null) {
-					shortest = true;
-					args.add("-loop"); args.add("1");
-					args.add("-i"); args.add(defaultInterviewImageFile.getCanonicalPath());
-				} else {
-					args.add("-i"); args.add(pods[i]);
+				StringBuilder cols = new StringBuilder();
+				StringBuilder rows = new StringBuilder();
+				for (int i = 0, j = 0; i < N; ++i) {
+					args.add("-i");
+					args.add(pods.get(i));
+					cols.append('[').append(i).append(":v]");
+					if (i != 0 && i % w == 0) {
+						cols.append("hstack[c").append(j).append("];");
+						rows.append("[c").append(j).append(']');
+						j++;
+					}
+					if (i == N - 1) {
+						if (j == 0) {
+							cols.append("hstack[v]");
+						} else {
+							rows.append("vstack[v]");
+						}
+					}
 				}
+				args.add("-i");
+				args.add(wav.getCanonicalPath());
+				args.add("-filter_complex");
+				args.add(cols.append(rows).toString());
+				args.add("-map");
+				args.add("[v]");
 			}
-			args.add("-i"); args.add(wav.getCanonicalPath());
-			args.add("-filter_complex");
-			args.add(String.format("[0:v]scale=%1$d:%2$d,pad=2*%1$d:%2$d[left];[1:v]scale=%1$d:%2$d[right];[left][right]overlay=main_w/2:0%3$s"
-					, flvWidth, flvHeight, shortest ? ":shortest=1" : ""));
-			if (shortest) {
-				args.add("-shortest");
-			}
-			args.add("-map"); args.add("0:0");
-			args.add("-map"); args.add("1:0");
-			args.add("-map"); args.add("2:0");
+			args.add("-map");
+			args.add(String.format("%s:a", N));
 			args.add("-qmax"); args.add("1");
 			args.add("-qmin"); args.add("1");
 
-			r.setWidth(2 * flvWidth);
-			r.setHeight(flvHeight);
+			r.setWidth(w * width);
+			r.setHeight((N / w) * height);
 
 			String mp4path = convertToMp4(r, args, logs);
 
-			postProcess(r, mp4path, logs, waveFiles);
+			finalize(r, mp4path, logs);
 		} catch (Exception err) {
 			log.error("[startConversion]", err);
 			r.setStatus(Recording.Status.ERROR);
+		} finally {
+			if (Recording.Status.CONVERTING == r.getStatus()) {
+				r.setStatus(Recording.Status.ERROR);
+			}
+			postProcess(r, logs);
+			postProcess(waveFiles);
+			recordingDao.update(r);
+		}
+	}
+
+	private static class PodPart {
+		final String file;
+		final long duration;
+
+		public PodPart(String file) {
+			this.file = file;
+			this.duration = 0L;
+		}
+
+		public PodPart(long duration) {
+			this.file = null;
+			this.duration = duration;
+		}
+
+		public String getFile() {
+			return file;
+		}
+
+		public long getDuration() {
+			return duration;
+		}
+
+		public static void add(List<PodPart> parts, long duration) {
+			if (duration != 0L) {
+				parts.add(new PodPart(duration));
+			}
 		}
-		recordingDao.update(r);
 	}
 }
diff --git a/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/RecordingConverter.java b/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/RecordingConverter.java
index 0ff2511..40c37df 100644
--- a/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/RecordingConverter.java
+++ b/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/RecordingConverter.java
@@ -19,7 +19,6 @@
 package org.apache.openmeetings.core.converter;
 
 import static org.apache.openmeetings.util.OmFileHelper.EXTENSION_FLV;
-import static org.apache.openmeetings.util.OmFileHelper.getStreamsHibernateDir;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.getWebAppRootKey;
 
 import java.io.File;
@@ -34,7 +33,6 @@ import org.apache.openmeetings.db.entity.record.Recording;
 import org.apache.openmeetings.db.entity.record.RecordingMetaData;
 import org.apache.openmeetings.db.entity.record.RecordingMetaData.Status;
 import org.apache.openmeetings.util.OmFileHelper;
-import org.apache.openmeetings.util.process.ProcessHelper;
 import org.apache.openmeetings.util.process.ProcessResultList;
 import org.apache.wicket.util.string.Strings;
 import org.red5.logging.Red5LoggerFactory;
@@ -59,11 +57,11 @@ public class RecordingConverter extends BaseConverter implements IRecordingConve
 			log.warn("Conversion is NOT started. Recording with ID {} is not found", id);
 			return;
 		}
+		ProcessResultList logs = new ProcessResultList();
+		List<File> waveFiles = new ArrayList<>();
 		try {
 			log.debug("recording {}", r.getId());
 
-			ProcessResultList logs = new ProcessResultList();
-			List<File> waveFiles = new ArrayList<>();
 			File streamFolder = getStreamFolder(r);
 
 			RecordingMetaData screenMetaData = metaDataDao.getScreenByRecording(r.getId());
@@ -84,28 +82,10 @@ public class RecordingConverter extends BaseConverter implements IRecordingConve
 
 			screenMetaData = waitForTheStream(screenMetaData.getId());
 
-			stripAudioFirstPass(r, logs, waveFiles, streamFolder);
-
 			// Merge Wave to Full Length
 			File wav = new File(streamFolder, screenMetaData.getStreamName() + "_FINAL_WAVE.wav");
+			createWav(r, logs, streamFolder, waveFiles, wav);
 
-			if (waveFiles.isEmpty()) {
-				// create default Audio to merge it. strip to content length
-				String oneSecWav = new File(getStreamsHibernateDir(), "one_second.wav").getCanonicalPath();
-
-				// Calculate delta at beginning
-				double duration = diffSeconds(r.getRecordEnd(), r.getRecordStart());
-
-				String[] cmd = new String[] { getPathToSoX(), oneSecWav, wav.getCanonicalPath(), "pad", "0", String.valueOf(duration) };
-
-				logs.add(ProcessHelper.executeScript("generateSampleAudio", cmd));
-			} else if (waveFiles.size() == 1) {
-				wav = waveFiles.get(0);
-			} else {
-				String[] soxArgs = mergeAudioToWaves(waveFiles, wav);
-
-				logs.add(ProcessHelper.executeScript("mergeAudioToWaves", soxArgs));
-			}
 			screenMetaData.setFullWavAudioData(wav.getName());
 			metaDataDao.update(screenMetaData);
 
@@ -138,11 +118,13 @@ public class RecordingConverter extends BaseConverter implements IRecordingConve
 					"-i", inputScreenFullFlv, "-i", wav.getCanonicalPath()
 					), logs);
 
-			postProcess(r, mp4path, logs, waveFiles);
+			finalize(r, mp4path, logs);
 		} catch (Exception err) {
 			log.error("[startConversion]", err);
 			r.setStatus(Recording.Status.ERROR);
 		}
+		postProcess(r, logs);
+		postProcess(waveFiles);
 		recordingDao.update(r);
 	}
 }
diff --git a/openmeetings-core/src/main/java/org/apache/openmeetings/core/data/file/FileProcessor.java b/openmeetings-core/src/main/java/org/apache/openmeetings/core/data/file/FileProcessor.java
index e3fca3e..7f97834 100644
--- a/openmeetings-core/src/main/java/org/apache/openmeetings/core/data/file/FileProcessor.java
+++ b/openmeetings-core/src/main/java/org/apache/openmeetings/core/data/file/FileProcessor.java
@@ -118,8 +118,8 @@ public class FileProcessor {
 					log.debug("uploaded chart file"); // NOT implemented yet
 					break;
 				case Image:
-					// convert it to JPG
-					log.debug("##### convert it to JPG: ");
+					// convert it to PNG
+					log.debug("##### convert it to PNG: ");
 					copyFile(temp, file);
 					imageConverter.convertImage(f, sf);
 					break;
diff --git a/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/MobileService.java b/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/MobileService.java
index 8c61176..ed85d9c 100644
--- a/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/MobileService.java
+++ b/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/MobileService.java
@@ -275,7 +275,7 @@ public class MobileService {
 					add(map, "login", c.getLogin());
 					add(map, "email", c.getEmail());
 					add(map, "avsettings", c.getAvsettings());
-					add(map, "interviewPodId", c.getInterviewPodId());
+					add(map, "interviewPodId", null);
 					add(map, "vWidth", c.getWidth());
 					add(map, "vHeight", c.getHeight());
 					result.add(map);
@@ -351,7 +351,7 @@ public class MobileService {
 		return result;
 	}
 
-	public Map<String, Object> updateAvMode(String avMode, String width, String height, Integer interviewPodId) {
+	public Map<String, Object> updateAvMode(String avMode, String width, String height, @SuppressWarnings("unused") Integer interviewPodId) {
 		IConnection current = Red5.getConnectionLocal();
 		StreamClient c = streamClientManager.get(IClientUtil.getId(current.getClient()));
 		c.setAvsettings(avMode);
@@ -361,9 +361,6 @@ public class MobileService {
 		}
 		c.setWidth(Double.valueOf(width).intValue());
 		c.setHeight(Double.valueOf(height).intValue());
-		if (interviewPodId > 0) {
-			c.setInterviewPodId(interviewPodId);
-		}
 		streamClientManager.update(c);
 		Map<String, Object> hsm = new HashMap<>();
 		hsm.put("client", c);
diff --git a/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/ScopeApplicationAdapter.java b/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/ScopeApplicationAdapter.java
index cd76dd4..a7340a5 100644
--- a/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/ScopeApplicationAdapter.java
+++ b/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/ScopeApplicationAdapter.java
@@ -521,7 +521,7 @@ public class ScopeApplicationAdapter extends MultiThreadedApplicationAdapter imp
 			}
 			streamClientManager.update(c);
 			if (Client.Type.sharing == c.getType() && c.isRecordingStarted()) {
-				recordingService.startRecording(current.getScope(), c, false);
+				recordingService.startRecording(current.getScope(), c);
 			}
 
 			_log.debug("newStream SEND: {}", c);
@@ -539,7 +539,7 @@ public class ScopeApplicationAdapter extends MultiThreadedApplicationAdapter imp
 					}
 
 					_log.debug("check send to {}", rcl);
-					if (rcl.isRecordingStarted()) {
+					if (IClientUtil.getRecordingId(current.getScope()) != null) {
 						_log.debug("RCL getIsRecording newStream SEND");
 						recordingService.startStreamRecord(current);
 					}
@@ -935,7 +935,7 @@ public class ScopeApplicationAdapter extends MultiThreadedApplicationAdapter imp
 			return;
 		}
 
-		recordingService.startRecording(getChildScope(c.getRoomId()), c, true);
+		recordingService.startRecording(getChildScope(c.getRoomId()), c);
 	}
 
 	/**
diff --git a/openmeetings-core/src/main/java/org/apache/openmeetings/core/service/RecordingService.java b/openmeetings-core/src/main/java/org/apache/openmeetings/core/service/RecordingService.java
index ca93b4e..3b99d55 100644
--- a/openmeetings-core/src/main/java/org/apache/openmeetings/core/service/RecordingService.java
+++ b/openmeetings-core/src/main/java/org/apache/openmeetings/core/service/RecordingService.java
@@ -41,6 +41,7 @@ import org.apache.openmeetings.db.entity.file.BaseFileItem.Type;
 import org.apache.openmeetings.db.entity.record.Recording;
 import org.apache.openmeetings.db.entity.record.RecordingMetaData;
 import org.apache.openmeetings.db.entity.record.RecordingMetaData.Status;
+import org.apache.openmeetings.db.entity.room.Room;
 import org.apache.openmeetings.db.entity.room.StreamClient;
 import org.apache.openmeetings.db.entity.user.User;
 import org.apache.openmeetings.db.manager.IClientManager;
@@ -94,10 +95,10 @@ public class RecordingService {
 		return "rec_" + recordingId + "_stream_" + streamid + "_" + dateString;
 	}
 
-	public void startRecording(final IScope scope, IClient client, boolean isInterview) {
+	public void startRecording(final IScope scope, IClient client) {
 		try {
 			log.debug("##REC:: recordMeetingStream ::");
-
+			boolean isInterview = Room.Type.interview == client.getRoomType();
 			Long roomId = client.getRoomId();
 
 			Date now = new Date();
@@ -158,6 +159,7 @@ public class RecordingService {
 	public void stopRecording(IScope scope, IClient client) {
 		try {
 			Long recordingId = IClientUtil.getRecordingId(scope);
+			IClientUtil.setRecordingId(scope, null);
 			log.debug("stopRecordAndSave {}, {}, ID: {}", client.getLogin(), client.getRemoteAddress(), recordingId);
 			if (recordingId == null) {
 				log.error("Unable to find recordingId on recording stop");
@@ -343,7 +345,7 @@ public class RecordingService {
 				if (rcl.isSharingStarted() || rcl.isRecordingStarted()) {
 					String streamName = generateFileName(recordingId, broadcastId);
 
-					Long metaId = metaDataDao.add(recordingId, now, false, false, true, streamName, null);
+					Long metaId = metaDataDao.add(recordingId, now, false, false, true, streamName, rcl.getSid());
 
 					// Start FLV Recording
 					addListener(conn, rcl.getBroadcastId(), streamName, metaId, true, isInterview);
@@ -357,8 +359,7 @@ public class RecordingService {
 				// But we only record av or a, video only is not interesting
 				String streamName = generateFileName(recordingId, broadcastId);
 
-				Long metaId = metaDataDao.add(recordingId, now, audioOnly, videoOnly, false, streamName,
-						rcl.getInterviewPodId());
+				Long metaId = metaDataDao.add(recordingId, now, audioOnly, videoOnly, false, streamName, rcl.getSid());
 
 				// Start FLV recording
 				addListener(conn, broadcastId, streamName, metaId, !audioOnly, isInterview);
diff --git a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/record/RecordingDao.java b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/record/RecordingDao.java
index 25b3f3a..85c3023 100644
--- a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/record/RecordingDao.java
+++ b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/record/RecordingDao.java
@@ -18,8 +18,8 @@
  */
 package org.apache.openmeetings.db.dao.record;
 
-import static org.apache.openmeetings.util.OmFileHelper.EXTENSION_JPG;
 import static org.apache.openmeetings.util.OmFileHelper.EXTENSION_MP4;
+import static org.apache.openmeetings.util.OmFileHelper.EXTENSION_PNG;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.getWebAppRootKey;
 
 import java.time.Duration;
@@ -189,8 +189,8 @@ public class RecordingDao extends BaseFileItemDao {
 	private long getSize(Recording r) {
 		long size = 0;
 
-		if (r.exists(EXTENSION_JPG)) {
-			size += r.getFile(EXTENSION_JPG).length();
+		if (r.exists(EXTENSION_PNG)) {
+			size += r.getFile(EXTENSION_PNG).length();
 		}
 		if (r.exists(EXTENSION_MP4)) {
 			size += r.getFile(EXTENSION_MP4).length();
diff --git a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/record/RecordingMetaDataDao.java b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/record/RecordingMetaDataDao.java
index 2a84d43..df7f02d 100644
--- a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/record/RecordingMetaDataDao.java
+++ b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/record/RecordingMetaDataDao.java
@@ -68,7 +68,7 @@ public class RecordingMetaDataDao {
 	}
 
 	public Long add(Long recordingId, Date recordStart, boolean isAudioOnly,
-			boolean isVideoOnly, boolean isScreenData, String streamName, Integer interiewPodId) {
+			boolean isVideoOnly, boolean isScreenData, String streamName, String sid) {
 		try {
 
 			RecordingMetaData metaData = new RecordingMetaData();
@@ -78,7 +78,7 @@ public class RecordingMetaDataDao {
 			metaData.setVideoOnly(isVideoOnly);
 			metaData.setScreenData(isScreenData);
 			metaData.setStreamName(streamName);
-			metaData.setInteriewPodId(interiewPodId);
+			metaData.setSid(sid);
 			metaData = update(metaData);
 			return metaData.getId();
 		} catch (Exception ex2) {
diff --git a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dto/room/CheckDto.java b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dto/room/CheckDto.java
index 1ef1d2b..dff1af1 100644
--- a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dto/room/CheckDto.java
+++ b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dto/room/CheckDto.java
@@ -27,7 +27,6 @@ import javax.xml.bind.annotation.XmlRootElement;
 
 import org.apache.openmeetings.db.entity.basic.Client;
 import org.apache.openmeetings.db.entity.basic.Client.Activity;
-import org.apache.openmeetings.db.entity.basic.Client.Pod;
 import org.apache.openmeetings.db.entity.room.Room;
 
 @XmlRootElement
@@ -37,7 +36,6 @@ public class CheckDto {
 	private final Room.Type roomType;
 	private final boolean audioOnly;
 	private final Set<Activity> activities = new HashSet<>();
-	private final Pod pod;
 
 	public CheckDto(Client c) {
 		roomId = c.getRoom().getId();
@@ -49,7 +47,6 @@ public class CheckDto {
 		if (c.hasActivity(Activity.broadcastV)) {
 			activities.add(Activity.broadcastV);
 		}
-		pod = c.getPod();
 	}
 
 	public long getRoomId() {
@@ -67,8 +64,4 @@ public class CheckDto {
 	public Set<Activity> getActivities() {
 		return activities;
 	}
-
-	public Pod getPod() {
-		return pod;
-	}
 }
diff --git a/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/basic/Client.java b/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/basic/Client.java
index cfba746..cfbd304 100644
--- a/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/basic/Client.java
+++ b/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/basic/Client.java
@@ -56,9 +56,6 @@ public class Client implements IClient {
 		, record
 		, publish //sends A/V to external server
 	}
-	public enum Pod {
-		none, left, right;
-	}
 	private final String sessionId;
 	private int pageId;
 	private User user;
@@ -70,7 +67,6 @@ public class Client implements IClient {
 	private final Set<Activity> activities = new ConcurrentHashSet<>();
 	private final Set<String> streams = new ConcurrentHashSet<>();
 	private final Date connectedSince;
-	private Pod pod;
 	private int cam = -1;
 	private int mic = -1;
 	private int width = 0;
@@ -282,14 +278,6 @@ public class Client implements IClient {
 		return this;
 	}
 
-	public Pod getPod() {
-		return pod;
-	}
-
-	public void setPod(Pod pod) {
-		this.pod = pod;
-	}
-
 	public boolean isCamEnabled() {
 		return cam > -1;
 	}
@@ -379,6 +367,11 @@ public class Client implements IClient {
 		return room == null ? null : room.getId();
 	}
 
+	@Override
+	public Room.Type getRoomType() {
+		return room == null ? null : room.getType();
+	}
+
 	public JSONObject toJson(boolean self) {
 		JSONObject u = new JSONObject();
 		if (user != null) {
@@ -400,7 +393,6 @@ public class Client implements IClient {
 				.put("uid", uid)
 				.put("rights", new JSONArray(rights))
 				.put("activities", new JSONArray(activities))
-				.put("pod", pod)
 				.put("width", width)
 				.put("height", height)
 				.put("self", self);
@@ -465,7 +457,6 @@ public class Client implements IClient {
 			streams.clear();
 			streams.addAll(ss);
 		}
-		pod = c.pod;
 		cam = c.cam;
 		mic = c.mic;
 		width = c.width;
@@ -506,6 +497,6 @@ public class Client implements IClient {
 	@Override
 	public String toString() {
 		return "Client [uid=" + uid + ", sessionId=" + sessionId + ", pageId=" + pageId + ", userId=" + user.getId() + ", room=" + (room == null ? null : room.getId())
-				+ ", rights=" + rights + ", activities=" + activities + ", connectedSince=" + connectedSince + ", pod = " + pod + "]";
+				+ ", rights=" + rights + ", activities=" + activities + ", connectedSince=" + connectedSince + "]";
 	}
 }
diff --git a/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/basic/IClient.java b/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/basic/IClient.java
index 328f67c..59f6749 100644
--- a/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/basic/IClient.java
+++ b/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/basic/IClient.java
@@ -19,6 +19,7 @@
 package org.apache.openmeetings.db.entity.basic;
 
 import org.apache.openmeetings.db.entity.IDataProviderEntity;
+import org.apache.openmeetings.db.entity.room.Room;
 
 /**
  * Temporary interface, will be removed after 2 types of cliens will be merged
@@ -43,6 +44,7 @@ public interface IClient extends IDataProviderEntity {
 	String getLastname();
 	String getRemoteAddress();
 	Long getRoomId();
+	Room.Type getRoomType();
 	int getWidth();
 	int getHeight();
 	void setRecordingStarted(boolean recordingStarted);
diff --git a/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/file/BaseFileItem.java b/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/file/BaseFileItem.java
index 0521866..6b9605c 100644
--- a/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/file/BaseFileItem.java
+++ b/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/file/BaseFileItem.java
@@ -271,7 +271,14 @@ public abstract class BaseFileItem extends HistoricalEntity {
 					f = new File(getUploadWmlDir(), String.format(FILE_NAME_FMT, getHash(), ext == null ? EXTENSION_WML : ext));
 					break;
 				case Image:
-					f = new File(d, String.format(FILE_NAME_FMT, getHash(), ext == null ? EXTENSION_JPG : ext));
+					if (ext == null) {
+						f = new File(d, String.format(FILE_NAME_FMT, getHash(), EXTENSION_PNG));
+						if (!f.exists()) {
+							f = new File(d, String.format(FILE_NAME_FMT, getHash(), EXTENSION_JPG)); // backward compatibility
+						}
+					} else {
+						f = new File(d, String.format(FILE_NAME_FMT, getHash(), ext));
+					}
 					break;
 				case Recording:
 					f = new File(getStreamsHibernateDir(), String.format(FILE_NAME_FMT, getHash(), ext == null ? EXTENSION_MP4 : ext));
@@ -364,7 +371,7 @@ public abstract class BaseFileItem extends HistoricalEntity {
 		Set<String> exclusions = new HashSet<>();
 
 		OriginalFilter() {
-			exclusions.add(EXTENSION_JPG);
+			exclusions.add(EXTENSION_PNG);
 			exclusions.add("swf");
 			if (Type.Presentation == getType()) {
 				exclusions.add(EXTENSION_PDF);
diff --git a/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/record/RecordingMetaData.java b/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/record/RecordingMetaData.java
index b3cc956..264e828 100644
--- a/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/record/RecordingMetaData.java
+++ b/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/record/RecordingMetaData.java
@@ -93,6 +93,10 @@ public class RecordingMetaData extends HistoricalEntity {
 	@Element(data = true)
 	private String streamName;
 
+	@Column(name = "sid")
+	@Element(data = true)
+	private String sid;
+
 	@Column(name = "is_audio_only", nullable = false)
 	@Element(name = "isAudioOnly", data = true)
 	private boolean audioOnly;
@@ -113,10 +117,6 @@ public class RecordingMetaData extends HistoricalEntity {
 	@Element(name="audioIsValid", data = true, required = false)
 	private boolean audioValid;
 
-	@Column(name = "interiew_pod_id")
-	@Element(data = true, required = false)
-	private Integer interiewPodId;
-
 	/**
 	 * this is only STOPPED when the asynchronous stream writer's have completed to write packets to the file.
 	 */
@@ -190,6 +190,14 @@ public class RecordingMetaData extends HistoricalEntity {
 		this.streamName = streamName;
 	}
 
+	public String getSid() {
+		return sid;
+	}
+
+	public void setSid(String sid) {
+		this.sid = sid;
+	}
+
 	public String getFullWavAudioData() {
 		return fullWavAudioData;
 	}
@@ -206,14 +214,6 @@ public class RecordingMetaData extends HistoricalEntity {
 		this.audioValid = audioValid;
 	}
 
-	public Integer getInteriewPodId() {
-		return interiewPodId;
-	}
-
-	public void setInteriewPodId(Integer interiewPodId) {
-		this.interiewPodId = interiewPodId;
-	}
-
 	public Status getStreamStatus() {
 		return streamStatus;
 	}
diff --git a/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/room/Room.java b/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/room/Room.java
index 84c4bac..652713a 100644
--- a/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/room/Room.java
+++ b/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/room/Room.java
@@ -265,10 +265,6 @@ public class Room extends HistoricalEntity {
 	@Element(data = true, required = false)
 	private boolean filesOpened;
 
-	@Column(name = "auto_video_select", nullable = false)
-	@Element(data = true, required = false)
-	private boolean autoVideoSelect;
-
 	@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
 	@JoinColumn(name = "roomId")
 	@ForeignKey(enabled = true)
@@ -525,14 +521,6 @@ public class Room extends HistoricalEntity {
 		this.filesOpened = filesOpened;
 	}
 
-	public boolean isAutoVideoSelect() {
-		return autoVideoSelect;
-	}
-
-	public void setAutoVideoSelect(boolean autoVideoSelect) {
-		this.autoVideoSelect = autoVideoSelect;
-	}
-
 	public boolean isSipEnabled() {
 		return sipEnabled;
 	}
diff --git a/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/room/StreamClient.java b/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/room/StreamClient.java
index 2c5e9a6..8e9cc37 100644
--- a/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/room/StreamClient.java
+++ b/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/room/StreamClient.java
@@ -63,11 +63,11 @@ public class StreamClient implements IClient {
 	private Long metaId;
 	private String externalUserId;
 	private String externalUserType;
-	private Integer interviewPodId = null;
 	private boolean allowRecording = true;
 	private boolean micMuted = false;
 	private String serverId;
 	private Long roomId;
+	private Room.Type roomType;
 	private Type type = Type.video;
 
 	@Override
@@ -352,14 +352,6 @@ public class StreamClient implements IClient {
 		this.externalUserType = externalUserType;
 	}
 
-	public Integer getInterviewPodId() {
-		return interviewPodId;
-	}
-
-	public void setInterviewPodId(Integer interviewPodId) {
-		this.interviewPodId = interviewPodId;
-	}
-
 	public boolean isAllowRecording() {
 		return allowRecording;
 	}
@@ -390,6 +382,15 @@ public class StreamClient implements IClient {
 		return roomId;
 	}
 
+	@Override
+	public Room.Type getRoomType() {
+		return roomType;
+	}
+
+	public void setRoomType(Room.Type roomType) {
+		this.roomType = roomType;
+	}
+
 	public Type getType() {
 		return type;
 	}
diff --git a/openmeetings-install/src/main/java/org/apache/openmeetings/backup/BackupImport.java b/openmeetings-install/src/main/java/org/apache/openmeetings/backup/BackupImport.java
index 6444f0f..99174bb 100644
--- a/openmeetings-install/src/main/java/org/apache/openmeetings/backup/BackupImport.java
+++ b/openmeetings-install/src/main/java/org/apache/openmeetings/backup/BackupImport.java
@@ -1089,7 +1089,7 @@ public class BackupImport {
 
 	private static File getImgDir(String name) {
 		int start = name.startsWith(THUMB_IMG_PREFIX) ? THUMB_IMG_PREFIX.length() : 0;
-		String hash = name.substring(start, name.length() - EXTENSION_JPG.length() - 1);
+		String hash = name.substring(start, name.length() - EXTENSION_PNG.length() - 1);
 		return new File(getUploadFilesDir(), hash);
 	}
 
diff --git a/openmeetings-util/src/main/java/org/apache/openmeetings/util/OmFileHelper.java b/openmeetings-util/src/main/java/org/apache/openmeetings/util/OmFileHelper.java
index 0d6b1d7..6cf0db9 100644
--- a/openmeetings-util/src/main/java/org/apache/openmeetings/util/OmFileHelper.java
+++ b/openmeetings-util/src/main/java/org/apache/openmeetings/util/OmFileHelper.java
@@ -55,7 +55,7 @@ public class OmFileHelper {
 	public static final String PROFILES_PREFIX = "profile_";
 	public static final String LANG_FILE_NAME = "languages.xml";
 	public static final String LIBRARY_FILE_NAME = "library.xml";
-	public static final String PROFILE_IMG_NAME = "profile.jpg";
+	public static final String PROFILE_IMG_NAME = "profile.png";
 	public static final String PROFILE_FILE_NAME = "profile";
 	public static final String RECORDING_FILE_NAME = "flvRecording_";
 	public static final String THUMB_IMG_PREFIX = "_thumb_";
diff --git a/openmeetings-util/src/main/java/org/apache/openmeetings/util/StoredFile.java b/openmeetings-util/src/main/java/org/apache/openmeetings/util/StoredFile.java
index 6a7b3dc..13af4eb 100644
--- a/openmeetings-util/src/main/java/org/apache/openmeetings/util/StoredFile.java
+++ b/openmeetings-util/src/main/java/org/apache/openmeetings/util/StoredFile.java
@@ -19,7 +19,7 @@
 package org.apache.openmeetings.util;
 
 import static org.apache.openmeetings.util.OmFileHelper.FILE_NAME_FMT;
-import static org.apache.openmeetings.util.OmFileHelper.JPG_MIME_TYPE;
+import static org.apache.openmeetings.util.OmFileHelper.PNG_MIME_TYPE;
 import static org.apache.openmeetings.util.OmFileHelper.getFileExt;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.getWebAppRootKey;
 import static org.apache.tika.metadata.TikaMetadataKeys.RESOURCE_NAME_KEY;
@@ -54,10 +54,10 @@ public class StoredFile {
 			application("x-tika-msoffice"), application("x-tika-ooxml"), application("msword")
 			, application("vnd.wordperfect"), application("rtf")));
 
-	private static final MediaType MIME_JPG = MediaType.parse(JPG_MIME_TYPE);
+	private static final MediaType MIME_PNG = MediaType.parse(PNG_MIME_TYPE);
 	private static final Set<MediaType> PDF_TYPES = new HashSet<>(Arrays.asList(application("pdf"), application("postscript")));
 	private static final Set<MediaType> CHART_TYPES = new HashSet<>();
-	private static final Set<MediaType> AS_IS_TYPES = new HashSet<>(Arrays.asList(MIME_JPG));
+	private static final Set<MediaType> AS_IS_TYPES = new HashSet<>(Arrays.asList(MIME_PNG));
 	private static final String ACCEPT_STRING;
 	private static TikaConfig tika;
 	static {
@@ -154,11 +154,11 @@ public class StoredFile {
 		return PDF_TYPES.contains(mime);
 	}
 
-	public boolean isJpg() {
+	public boolean isPng() {
 		if (mime == null) {
 			return false;
 		}
-		return MIME_JPG.equals(mime);
+		return MIME_PNG.equals(mime);
 	}
 
 	public boolean isImage() {
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/rooms/RoomsPanel.html b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/rooms/RoomsPanel.html
index 626820e..0414fe7 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/rooms/RoomsPanel.html
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/rooms/RoomsPanel.html
@@ -135,9 +135,6 @@
 					<div class="formelement">
 						<label wicket:for="filesOpened"><wicket:message key="1516" /></label><input type="checkbox" class="formcheckbox" wicket:id="filesOpened" />
 					</div>
-					<div class="formelement">
-						<label wicket:for="autoVideoSelect"><wicket:message key="1528" /></label><input type="checkbox" class="formcheckbox" wicket:id="autoVideoSelect" />
-					</div>
 				</fieldset>
 
 				<!-- Room files -->
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/StreamClientManager.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/StreamClientManager.java
index 2c59781..4c70cd7 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/StreamClientManager.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/StreamClientManager.java
@@ -42,7 +42,6 @@ import org.apache.openmeetings.db.dao.server.SessiondataDao;
 import org.apache.openmeetings.db.dao.user.UserDao;
 import org.apache.openmeetings.db.entity.basic.Client;
 import org.apache.openmeetings.db.entity.basic.Client.Activity;
-import org.apache.openmeetings.db.entity.basic.Client.Pod;
 import org.apache.openmeetings.db.entity.basic.IClient;
 import org.apache.openmeetings.db.entity.room.Room;
 import org.apache.openmeetings.db.entity.room.Room.Right;
@@ -177,6 +176,7 @@ public class StreamClientManager implements IStreamClientManager {
 		}
 		User u = client.getUser();
 		rcl.setUserId(u.getId());
+		rcl.setRoomType(client.getRoomType());
 		rcl.setLogin(u.getLogin());
 		rcl.setFirstname(u.getFirstname());
 		rcl.setLastname(u.getLastname());
@@ -194,9 +194,6 @@ public class StreamClientManager implements IStreamClientManager {
 				rcl.setWidth(client.getWidth());
 				rcl.setHeight(client.getHeight());
 			}
-			if (client.getPod() != Pod.none) {
-				rcl.setInterviewPodId(client.getPod() == Pod.left ? 1 : 2);
-			}
 			StringBuilder sb = new StringBuilder();
 			if (client.hasActivity(Activity.broadcastA)) {
 				sb.append('a');
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomResourceReference.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomResourceReference.java
index 01e54bb..81ad1b5 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomResourceReference.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomResourceReference.java
@@ -21,10 +21,9 @@ package org.apache.openmeetings.web.room;
 import static org.apache.openmeetings.db.dto.room.Whiteboard.ATTR_FILE_ID;
 import static org.apache.openmeetings.db.dto.room.Whiteboard.ATTR_SLIDE;
 import static org.apache.openmeetings.util.OmFileHelper.EXTENSION_PNG;
-import static org.apache.openmeetings.util.OmFileHelper.JPG_MIME_TYPE;
 import static org.apache.openmeetings.util.OmFileHelper.MP4_MIME_TYPE;
 import static org.apache.openmeetings.util.OmFileHelper.PNG_MIME_TYPE;
-import static org.apache.openmeetings.util.OmFileHelper.getOmHome;
+import static org.apache.openmeetings.util.OmFileHelper.getImagesDir;
 import static org.apache.openmeetings.web.app.Application.getBean;
 import static org.apache.openmeetings.web.app.WebSession.getUserId;
 
@@ -68,7 +67,7 @@ public class RoomResourceReference extends FileItemResourceReference<FileItem> {
 				mime = "application/xml";
 				break;
 			case Image:
-				mime = JPG_MIME_TYPE;
+				mime = PNG_MIME_TYPE;
 				break;
 			case Presentation:
 				mime = PNG_MIME_TYPE;
@@ -124,7 +123,7 @@ public class RoomResourceReference extends FileItemResourceReference<FileItem> {
 	protected File getFile(FileItem f, String ext) {
 		File file = f.getFile(ext);
 		if (file == null || !file.exists()) {
-			file = new File(new File(getOmHome(), "default"), String.format("deleted.%s", EXTENSION_PNG));
+			file = new File(getImagesDir(), String.format("deleted.%s", EXTENSION_PNG));
 		}
 		return file;
 	}
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomSidebar.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomSidebar.java
index 33b7574..bd706c9 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomSidebar.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomSidebar.java
@@ -29,7 +29,6 @@ import java.util.ArrayList;
 
 import org.apache.openmeetings.core.util.WebSocketHelper;
 import org.apache.openmeetings.db.entity.basic.Client;
-import org.apache.openmeetings.db.entity.basic.Client.Pod;
 import org.apache.openmeetings.db.entity.room.Room;
 import org.apache.openmeetings.db.entity.room.Room.Right;
 import org.apache.openmeetings.db.entity.room.Room.RoomElement;
@@ -79,7 +78,6 @@ public class RoomSidebar extends Panel {
 	public static final String PARAM_ACTIVITY = "activity";
 	public static final String PARAM_RIGHT = "right";
 	public static final String PARAM_UID = "uid";
-	public static final String PARAM_POD = "pod";
 	public static final String PARAM_SETTINGS = "s";
 	private final RoomPanel room;
 	private UploadDialog upload;
@@ -197,10 +195,8 @@ public class RoomSidebar extends Panel {
 					return;
 				}
 				Client.Activity a = Client.Activity.valueOf(getRequest().getRequestParameters().getParameterValue(PARAM_ACTIVITY).toString());
-				StringValue podStr = getRequest().getRequestParameters().getParameterValue(PARAM_POD);
-				Pod pod = podStr.isEmpty() ? Pod.none : Pod.valueOf(podStr.toString());
 				Client c = getBean(ClientManager.class).get(uid);
-				toggleActivity(c, a, pod);
+				toggleActivity(c, a);
 			} catch (Exception e) {
 				log.error("Unexpected exception while toggle 'activity'", e);
 			}
@@ -219,7 +215,7 @@ public class RoomSidebar extends Panel {
 				if (!avInited) {
 					avInited = true;
 					if (Room.Type.conference == room.getRoom().getType()) {
-						toggleActivity(c, Client.Activity.broadcastAV, Pod.none);
+						toggleActivity(c, Client.Activity.broadcastAV);
 					}
 				}
 				sendUpdatedClient(c);
@@ -272,7 +268,7 @@ public class RoomSidebar extends Panel {
 	public void renderHead(IHeaderResponse response) {
 		super.renderHead(response);
 		response.render(new PriorityHeaderItem(getNamedFunction(FUNC_TOGGLE_RIGHT, toggleRight, explicit(PARAM_RIGHT), explicit(PARAM_UID))));
-		response.render(new PriorityHeaderItem(getNamedFunction(FUNC_TOGGLE_ACTIVITY, toggleActivity, explicit(PARAM_ACTIVITY), explicit(PARAM_UID), explicit(PARAM_POD))));
+		response.render(new PriorityHeaderItem(getNamedFunction(FUNC_TOGGLE_ACTIVITY, toggleActivity, explicit(PARAM_ACTIVITY), explicit(PARAM_UID))));
 		response.render(new PriorityHeaderItem(getNamedFunction(FUNC_ACTION, roomAction, explicit(PARAM_ACTION), explicit(PARAM_UID))));
 		response.render(new PriorityHeaderItem(getNamedFunction(FUNC_SETTINGS, avSettings, explicit(PARAM_SETTINGS))));
 	}
@@ -316,7 +312,7 @@ public class RoomSidebar extends Panel {
 		upload.open(handler);
 	}
 
-	public void toggleActivity(Client c, Client.Activity a, Pod _pod) {
+	public void toggleActivity(Client c, Client.Activity a) {
 		if (c == null) {
 			return;
 		}
@@ -338,14 +334,7 @@ public class RoomSidebar extends Panel {
 			if (a == Client.Activity.broadcastAV && !c.isMicEnabled() && !c.isCamEnabled()) {
 				return;
 			}
-			Pod pod = c.getPod();
-			c.setPod(_pod);
-			if (pod != null && pod != Pod.none && pod != c.getPod()) {
-				//pod has changed, no need to toggle
-				c.set(a);
-			} else {
-				c.toggle(a);
-			}
+			c.toggle(a);
 			room.broadcast(c); //will update client
 		}
 	}
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/icon/activity/RoomActivityIcon.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/icon/activity/RoomActivityIcon.java
index ba851ad..2ebec59 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/icon/activity/RoomActivityIcon.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/icon/activity/RoomActivityIcon.java
@@ -22,7 +22,6 @@ import static org.apache.openmeetings.web.room.sidebar.RoomSidebar.FUNC_TOGGLE_A
 import static org.apache.openmeetings.web.room.sidebar.RoomSidebar.activityAllowed;
 
 import org.apache.openmeetings.db.entity.basic.Client.Activity;
-import org.apache.openmeetings.db.entity.basic.Client.Pod;
 import org.apache.openmeetings.db.entity.room.Room;
 import org.apache.openmeetings.web.room.sidebar.icon.ClientIcon;
 
@@ -39,7 +38,7 @@ public abstract class RoomActivityIcon extends ClientIcon {
 
 	@Override
 	protected String getScript() {
-		return String.format("%s('%s', '%s', '%s');", FUNC_TOGGLE_ACTIVITY, activity.name(), uid, Pod.none.name());
+		return String.format("%s('%s', '%s');", FUNC_TOGGLE_ACTIVITY, activity.name(), uid);
 	}
 
 	protected boolean visible() {
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/rooms/RoomsPanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/rooms/RoomsPanel.java
index 97a6bb2..0f6b51b 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/rooms/RoomsPanel.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/rooms/RoomsPanel.java
@@ -64,7 +64,7 @@ public class RoomsPanel extends UserPanel {
 			protected void populateItem(final ListItem<Client> item) {
 				Client client = item.getModelObject();
 				final Long userId = client.getUserId();
-				item.add(new Image("clientImage", new ByteArrayResource("image/jpeg") {
+				item.add(new Image("clientImage", new ByteArrayResource("image/png") {
 					private static final long serialVersionUID = 1L;
 
 					@Override
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/util/ProfileImageResourceReference.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/util/ProfileImageResourceReference.java
index bfa9a4d..47da0d1 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/util/ProfileImageResourceReference.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/util/ProfileImageResourceReference.java
@@ -18,7 +18,7 @@
  */
 package org.apache.openmeetings.web.util;
 
-import static org.apache.openmeetings.util.OmFileHelper.JPG_MIME_TYPE;
+import static org.apache.openmeetings.util.OmFileHelper.PNG_MIME_TYPE;
 import static org.apache.openmeetings.util.OmFileHelper.SIP_USER_ID;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.getWebAppRootKey;
 import static org.apache.openmeetings.web.app.Application.getBean;
@@ -77,7 +77,7 @@ public class ProfileImageResourceReference extends ResourceReference {
 
 	@Override
 	public IResource getResource() {
-		return new ByteArrayResource(JPG_MIME_TYPE) {
+		return new ByteArrayResource(PNG_MIME_TYPE) {
 			private static final long serialVersionUID = 1L;
 			private Long userId = null;
 			private String uri = null;
diff --git a/openmeetings-web/src/main/webapp/WEB-INF/web.xml b/openmeetings-web/src/main/webapp/WEB-INF/web.xml
index 209395a..a970d52 100644
--- a/openmeetings-web/src/main/webapp/WEB-INF/web.xml
+++ b/openmeetings-web/src/main/webapp/WEB-INF/web.xml
@@ -66,7 +66,7 @@
 		</init-param>
 		<init-param>
 			<param-name>ignorePaths</param-name>
-			<param-value>conf,css,default,docs,images,js,persistence,public,screenshare,streams,upload,uploadtemp,services,DownloadHandler</param-value>
+			<param-value>conf,css,docs,images,js,persistence,public,screenshare,streams,upload,uploadtemp,services,DownloadHandler</param-value>
 		</init-param>
 	</filter>
 	<filter-mapping>
diff --git a/openmeetings-web/src/main/webapp/images/delete.gif b/openmeetings-web/src/main/webapp/images/delete.gif
deleted file mode 100644
index b6922ac..0000000
Binary files a/openmeetings-web/src/main/webapp/images/delete.gif and /dev/null differ
diff --git a/openmeetings-web/src/main/webapp/default/deleted.odp b/openmeetings-web/src/main/webapp/images/deleted.odp
similarity index 100%
rename from openmeetings-web/src/main/webapp/default/deleted.odp
rename to openmeetings-web/src/main/webapp/images/deleted.odp
diff --git a/openmeetings-web/src/main/webapp/default/deleted.pdf b/openmeetings-web/src/main/webapp/images/deleted.pdf
similarity index 100%
rename from openmeetings-web/src/main/webapp/default/deleted.pdf
rename to openmeetings-web/src/main/webapp/images/deleted.pdf
diff --git a/openmeetings-web/src/main/webapp/default/deleted.png b/openmeetings-web/src/main/webapp/images/deleted.png
similarity index 100%
rename from openmeetings-web/src/main/webapp/default/deleted.png
rename to openmeetings-web/src/main/webapp/images/deleted.png
diff --git a/openmeetings-web/src/main/webapp/images/interview_webcam.png b/openmeetings-web/src/main/webapp/images/interview_webcam.png
new file mode 100644
index 0000000..431e38b
Binary files /dev/null and b/openmeetings-web/src/main/webapp/images/interview_webcam.png differ
diff --git a/openmeetings-web/src/main/webapp/images/interview_webcam.svg b/openmeetings-web/src/main/webapp/images/interview_webcam.svg
new file mode 100644
index 0000000..360bc5b
--- /dev/null
+++ b/openmeetings-web/src/main/webapp/images/interview_webcam.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" ?>
+<!--
+Author: DinosoftLabs
+License: Free for commercial use
+Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0
+-->
+<svg enable-background="new 0 0 52 52" id="Layer_1" version="1.1" viewBox="0 0 52 52" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g><g><g><path d="M26,44C14.4208984,44,5,34.5795898,5,23S14.4208984,2,26,2s21,9.4204102,21,21S37.5790901,44,26,44z M26,4     C15.5234375,4,7,12.5234375,7,23s8.5234375,19,19,19s19-8.5234375,19-19S36.4765625,4,26,4z"/></g></g><g><g><path d="M26,35c-6.6171875,0-12-5.3833008-12-12s5.3828125-12,12-12c1.2587891, [...]
diff --git a/openmeetings-web/src/main/webapp/images/profile.jpg b/openmeetings-web/src/main/webapp/images/profile.jpg
deleted file mode 100644
index 9b8161d..0000000
Binary files a/openmeetings-web/src/main/webapp/images/profile.jpg and /dev/null differ
diff --git a/openmeetings-web/src/main/webapp/images/profile.png b/openmeetings-web/src/main/webapp/images/profile.png
new file mode 100644
index 0000000..db5b6d6
Binary files /dev/null and b/openmeetings-web/src/main/webapp/images/profile.png differ
diff --git a/openmeetings-web/src/main/webapp/streams/hibernate/default_interview_image.fla b/openmeetings-web/src/main/webapp/streams/hibernate/default_interview_image.fla
deleted file mode 100644
index 31398dc..0000000
Binary files a/openmeetings-web/src/main/webapp/streams/hibernate/default_interview_image.fla and /dev/null differ
diff --git a/openmeetings-web/src/main/webapp/streams/hibernate/default_interview_image.jpg b/openmeetings-web/src/main/webapp/streams/hibernate/default_interview_image.jpg
deleted file mode 100644
index 4b0f26b..0000000
Binary files a/openmeetings-web/src/main/webapp/streams/hibernate/default_interview_image.jpg and /dev/null differ
diff --git a/openmeetings-web/src/main/webapp/streams/hibernate/default_interview_image.png b/openmeetings-web/src/main/webapp/streams/hibernate/default_interview_image.png
deleted file mode 100644
index e87d640..0000000
Binary files a/openmeetings-web/src/main/webapp/streams/hibernate/default_interview_image.png and /dev/null differ
diff --git a/openmeetings-web/src/test/java/org/apache/openmeetings/core/file/TestFileProcessor.java b/openmeetings-web/src/test/java/org/apache/openmeetings/core/file/TestFileProcessor.java
index a9eb226..2b77103 100644
--- a/openmeetings-web/src/test/java/org/apache/openmeetings/core/file/TestFileProcessor.java
+++ b/openmeetings-web/src/test/java/org/apache/openmeetings/core/file/TestFileProcessor.java
@@ -43,8 +43,8 @@ public class TestFileProcessor extends AbstractJUnitDefaults {
 	protected FileProcessor processor;
 
 	@Test
-	public void testProcessJpeg() throws Exception {
-		for (String ext : new String[] {null, "txt", "png"}) {
+	public void testProcessPng() throws Exception {
+		for (String ext : new String[] {null, "txt", "jpg"}) {
 			FileItem f = new FileItemDTO()
 					.setName(String.format(FILE_NAME_FMT, FILE_NAME, ext))
 					.setHash(UUID.randomUUID().toString())
diff --git a/openmeetings-web/src/test/java/org/apache/openmeetings/util/TestStoredFile.java b/openmeetings-web/src/test/java/org/apache/openmeetings/util/TestStoredFile.java
index 14e97e0..3ac217a 100644
--- a/openmeetings-web/src/test/java/org/apache/openmeetings/util/TestStoredFile.java
+++ b/openmeetings-web/src/test/java/org/apache/openmeetings/util/TestStoredFile.java
@@ -30,9 +30,9 @@ import org.junit.Test;
 
 public class TestStoredFile extends AbstractJUnitDefaults {
 	@Test
-	public void testJpeg() throws FileNotFoundException, IOException {
+	public void testPng() throws FileNotFoundException, IOException {
 		File f = getDefaultProfilePicture();
-		for (String ext : new String[] {null, "txt", "png"}) {
+		for (String ext : new String[] {null, "txt", "jpg"}) {
 			StoredFile sf = new StoredFile("test image", ext, f);
 			assertTrue("Type should be detected as image", sf.isImage());
 			assertTrue("Type should be detected as image", sf.isAsIs());
diff --git a/openmeetings-web/src/test/java/org/apache/openmeetings/webservice/TestRoomService.java b/openmeetings-web/src/test/java/org/apache/openmeetings/webservice/TestRoomService.java
index 63d74f7..b6cc1e7 100644
--- a/openmeetings-web/src/test/java/org/apache/openmeetings/webservice/TestRoomService.java
+++ b/openmeetings-web/src/test/java/org/apache/openmeetings/webservice/TestRoomService.java
@@ -125,7 +125,7 @@ public class TestRoomService extends AbstractWebServiceTest {
 	@Test
 	public void testCreateWithFiles2() throws IOException {
 		//lets create real file
-		CallResult<FileItemDTO> fileCall = createVerifiedFile(getDefaultProfilePicture(), "img.jpg", BaseFileItem.Type.Image);
+		CallResult<FileItemDTO> fileCall = createVerifiedFile(getDefaultProfilePicture(), "img.png", BaseFileItem.Type.Image);
 
 		Room.Type type = Room.Type.presentation;
 		String name = "Unit Test Ext Room4";