From f4954dd87149d0cbfd039c8be50a8f8f8a956a7b Mon Sep 17 00:00:00 2001
From: gnaegi <none@none>
Date: Mon, 25 Apr 2016 15:15:16 +0200
Subject: [PATCH] OO-725 rename type to resolution, use getter/setter
 conventions, missing i18n keys, fix modul initialization

---
 .../org/olat/modules/video/VideoManager.java  |  9 ++-
 .../org/olat/modules/video/VideoModule.java   | 49 ++++++++++++++-
 .../video/manager/VideoManagerImpl.java       | 17 +++---
 .../video/manager/VideoTranscodingTask.java   | 61 ++++++++++---------
 .../video/model/VideoQualityVersion.java      | 51 +++++++++-------
 .../modules/video/ui/QualityTableRow.java     | 12 ++--
 .../ui/VideoQualityTableFormController.java   | 19 +++---
 .../video/ui/VideoQualityTableModel.java      |  4 +-
 .../modules/video/ui/_content/video_run.html  |  5 +-
 .../video/ui/_i18n/LocalStrings_de.properties | 13 ++--
 .../video/ui/_i18n/LocalStrings_en.properties | 18 ++++--
 .../resources/serviceconfig/olat.properties   | 13 +++-
 12 files changed, 170 insertions(+), 101 deletions(-)

diff --git a/src/main/java/org/olat/modules/video/VideoManager.java b/src/main/java/org/olat/modules/video/VideoManager.java
index e4ccfbb9af9..b8d11a4fd20 100644
--- a/src/main/java/org/olat/modules/video/VideoManager.java
+++ b/src/main/java/org/olat/modules/video/VideoManager.java
@@ -148,11 +148,14 @@ public interface VideoManager {
 	 * Create and add a new transcoding version to the video resource. The
 	 * version is set to "isTransforming".
 	 * 
-	 * @param video The video resource
-	 * @param resolution The resolution for the transcoding process
+	 * @param video
+	 *            The video resource
+	 * @param resolution
+	 *            The resolution for the transcoding process. This is the height
+	 *            of the target video size (e.g. 720 for 720p resolution)
 	 * @return VideoQualityVersion for this new version
 	 */
-	public abstract VideoQualityVersion addNewVersionForTranscoding(OLATResource video, String resolution);
+	public abstract VideoQualityVersion addNewVersionForTranscoding(OLATResource video, int resolution);
 
 	/**
 	 * Update an already existing version or add as new version.
diff --git a/src/main/java/org/olat/modules/video/VideoModule.java b/src/main/java/org/olat/modules/video/VideoModule.java
index ae25344c09d..06fbc87487d 100644
--- a/src/main/java/org/olat/modules/video/VideoModule.java
+++ b/src/main/java/org/olat/modules/video/VideoModule.java
@@ -19,11 +19,18 @@
  */
 package org.olat.modules.video;
 
+import java.util.Arrays;
+
 import org.olat.core.configuration.AbstractSpringModule;
+import org.olat.core.logging.OLog;
+import org.olat.core.logging.Tracing;
 import org.olat.core.util.StringHelper;
 import org.olat.core.util.coordinate.CoordinatorManager;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.core.convert.ConversionService;
+import org.springframework.format.support.DefaultFormattingConversionService;
 import org.springframework.stereotype.Service;
 
 /**
@@ -39,8 +46,6 @@ public class VideoModule extends AbstractSpringModule {
 	private static final String VIDEO_ENABLED = "video.enabled";
 	private static final String VIDEOCOURSENODE_ENABLED = "video.coursenode.enabled";
 	private static final String VIDEOTRANSCODING_ENABLED = "video.transcoding.enabled";
-	@Value("${video.transcoding.provider:handbrake}")
-	private String transcodingProvider;
 
 	@Value("${video.enabled:true}")
 	private boolean enabled;
@@ -48,7 +53,18 @@ public class VideoModule extends AbstractSpringModule {
 	private boolean coursenodeEnabled;
 	@Value("${video.transcoding.enabled:false}")
 	private boolean transcodingEnabled;
-
+	@Value("${video.transcoding.resolutions}")
+	private int[] transcodingResolutions;
+	@Value("${video.transcoding.taskset.cpuconfig}")
+	private String transcodingTasksetConfig;
+	
+	@Bean
+	public static ConversionService conversionService() {
+		// needed to create the transcodingResolutions property by spring
+	    return new DefaultFormattingConversionService();
+	}
+	
+	private static final OLog log = Tracing.createLoggerFor(VideoModule.class);
 
 	@Autowired
 	public VideoModule(CoordinatorManager coordinatorManager) {
@@ -72,7 +88,33 @@ public class VideoModule extends AbstractSpringModule {
 			transcodingEnabled = "true".equals(enabledTranscodingObj);
 		}
 
+		log.info("video.enabled=" + isEnabled());
+		log.info("video.coursenode.enabled=" + isCoursenodeEnabled());
+		log.info("video.transcoding.enabled=" + isTranscodingEnabled());
+		log.info("video.transcoding.resolutions=" + Arrays.toString(getTranscodingResolutions()));
+		log.info("video.transcoding.taskset.cpuconfig=" + getTranscodingTasksetConfig());
+	}
+
+	/**
+	 * @return Array of transcoding resolutions. The values represent the target
+	 *         height of the transcoded video, 1080 for 1080p video size etc.
+	 */
+	public int[] getTranscodingResolutions() {
+		return transcodingResolutions;
 	}
+
+	/**
+	 * @return null to indicate that taskset is disabled or the -c options to control the number of cores, e.g. "0,1"
+	 */
+	public String getTranscodingTasksetConfig() {
+		if (StringHelper.containsNonWhitespace(transcodingTasksetConfig)) {
+			return transcodingTasksetConfig.trim();			
+		} else {
+			return null;
+		}
+	}
+
+
 	@Override
 	protected void initFromChangedProperties() {
 		init();
@@ -107,5 +149,6 @@ public class VideoModule extends AbstractSpringModule {
 	public void setTranscodingEnabled(boolean transcodingEnabled) {
 		this.transcodingEnabled = transcodingEnabled;
 		setStringProperty(VIDEOTRANSCODING_ENABLED, Boolean.toString(transcodingEnabled), true);
+		//TODO: check all video resources if there are missing versions
 	}
 }
diff --git a/src/main/java/org/olat/modules/video/manager/VideoManagerImpl.java b/src/main/java/org/olat/modules/video/manager/VideoManagerImpl.java
index 7333abba2d8..a9fb84fca39 100644
--- a/src/main/java/org/olat/modules/video/manager/VideoManagerImpl.java
+++ b/src/main/java/org/olat/modules/video/manager/VideoManagerImpl.java
@@ -40,7 +40,6 @@ import javax.imageio.ImageIO;
 
 import org.jcodec.api.FrameGrab;
 import org.jcodec.common.FileChannelWrapper;
-import org.olat.core.CoreSpringFactory;
 import org.olat.core.commons.services.image.Size;
 import org.olat.core.commons.services.taskexecutor.TaskExecutorManager;
 import org.olat.core.commons.services.video.MovieService;
@@ -88,6 +87,8 @@ public class VideoManagerImpl implements VideoManager {
 	private VideoModule videoModule;
 	@Autowired 
 	private RepositoryManager repositoryManager;
+	@Autowired
+	private TaskExecutorManager taskManager;
 	
 	private static final OLog log = Tracing.createLoggerFor(VideoManagerImpl.class);
 
@@ -327,14 +328,13 @@ public class VideoManagerImpl implements VideoManager {
 		//TODO: check for existing version, add option to force rebuild of all versions
 		Size size = getVideoSize(video);
 		int height = size.getHeight();
-		TaskExecutorManager taskManager = CoreSpringFactory.getImpl(TaskExecutorManager.class);
-		//TODO: add to module and admin console which version to generate
-		int[] resolutions = {1080, 720, 480, 360, 240, 144};
+		//TODO: GUI to admin console to manage transcoding resolutions
+		int[] resolutions = videoModule.getTranscodingResolutions();
 		for (int resolution : resolutions) {
 			if (height < resolution) {
 				continue;
 			}
-			VideoQualityVersion version = addNewVersionForTranscoding(video, Integer.toString(resolution));
+			VideoQualityVersion version = addNewVersionForTranscoding(video, resolution);
 			VideoTranscodingTask task = new VideoTranscodingTask(video, version);
 			taskManager.execute(task, null, video, null, new Date());		
 		}
@@ -554,10 +554,10 @@ public class VideoManagerImpl implements VideoManager {
 
 	
 	@Override
-	public VideoQualityVersion addNewVersionForTranscoding(OLATResource video, String resolution) {
+	public VideoQualityVersion addNewVersionForTranscoding(OLATResource video, int resolution) {
 		List<VideoQualityVersion> versions = getQualityVersions(video);
 		VideoQualityVersion version = new VideoQualityVersion(resolution, null, null, VideoManagerImpl.FILETYPE_MP4);
-		version.setIsTransforming(true);
+		version.setTranscodingStatus(VideoQualityVersion.TRANSCODING_STATUS_WAITING);
 		versions.add(version);
 		// Store on disk
 		VFSContainer optimizedDataContainer = getOptimizedDataContainer(video);
@@ -573,12 +573,11 @@ public class VideoManagerImpl implements VideoManager {
 		List<VideoQualityVersion> versions = getQualityVersions(video);
 		boolean found = false;
 		for (VideoQualityVersion existingVersion : versions) {
-			if (updatedVersion.getType().equals(existingVersion.getType())) {
+			if (updatedVersion.getResolution() == existingVersion.getResolution()) {
 				// update properties
 				existingVersion.setDimension(updatedVersion.getDimension());
 				existingVersion.setFileSize(updatedVersion.getFileSize());
 				existingVersion.setFormat(updatedVersion.getFormat());
-				existingVersion.setIsTransforming(updatedVersion.getIsTransforming());
 				existingVersion.setTranscodingStatus(updatedVersion.getTranscodingStatus());
 				found = true;
 				break;
diff --git a/src/main/java/org/olat/modules/video/manager/VideoTranscodingTask.java b/src/main/java/org/olat/modules/video/manager/VideoTranscodingTask.java
index 0a5c7490e81..9a91141a0bd 100644
--- a/src/main/java/org/olat/modules/video/manager/VideoTranscodingTask.java
+++ b/src/main/java/org/olat/modules/video/manager/VideoTranscodingTask.java
@@ -30,8 +30,6 @@ import org.olat.core.CoreSpringFactory;
 import org.olat.core.commons.services.image.Size;
 import org.olat.core.commons.services.taskexecutor.LongRunnable;
 import org.olat.core.commons.services.taskexecutor.Sequential;
-import org.olat.core.commons.services.taskexecutor.Task;
-import org.olat.core.commons.services.taskexecutor.TaskAwareRunnable;
 import org.olat.core.commons.services.video.MovieService;
 import org.olat.core.logging.OLog;
 import org.olat.core.logging.Tracing;
@@ -39,49 +37,52 @@ import org.olat.core.util.Formatter;
 import org.olat.core.util.vfs.LocalFileImpl;
 import org.olat.fileresource.FileResourceManager;
 import org.olat.modules.video.VideoManager;
+import org.olat.modules.video.VideoModule;
 import org.olat.modules.video.model.VideoQualityVersion;
 import org.olat.resource.OLATResource;
 
 /**
+ * This task implements transcoding of a single video file using the Handbrake CLI. 
+ * 
  * Initial date: 22.04.2016<br>
  * @author gnaegi, gnaegi@frentix.com, http://www.frentix.com
  *
  */
-public class VideoTranscodingTask implements LongRunnable, TaskAwareRunnable, Sequential {
+public class VideoTranscodingTask implements LongRunnable, Sequential {
+	private static final long serialVersionUID = 2982868860465334552L;
 	private static final OLog log = Tracing.createLoggerFor(VideoTranscodingTask.class);
-	private transient Task task;
 	private OLATResource video;
-	private String resolution;
 	private VideoQualityVersion version;
 	private File transcodedFile;
 	
+	/**
+	 * 
+	 * @param video
+	 * @param version
+	 */
 	VideoTranscodingTask(OLATResource video, VideoQualityVersion version) {
 		this.video = video;
 		this.version = version;
-		this.resolution = version.getType();
-	}
-
-
-	@Override
-	public void setTask(Task task) {
-		this.task = task;
 	}
 
 	
 	@Override
 	public void run() {
+		VideoModule videoModule = CoreSpringFactory.getImpl(VideoModule.class);
 		VideoManager videoManager = CoreSpringFactory.getImpl(VideoManager.class);
 		File masterFile = videoManager.getVideoFile(video);
 		FileResourceManager fileResourceManager = CoreSpringFactory.getImpl(FileResourceManager.class);		
 		File videoResourceFileroot = fileResourceManager.getFileResourceRoot(video);
 		File optimizedFolder = new File(videoResourceFileroot, VideoManagerImpl.DIRNAME_OPTIMIZED_VIDEO_DATA);
-		transcodedFile = new File(optimizedFolder,  resolution + masterFile.getName());
+		transcodedFile = new File(optimizedFolder,  Integer.toString(version.getResolution()) + masterFile.getName());
 		
-		ArrayList<String> cmd = new ArrayList<String>();
-		//TODO make configurable (taskset on osx not available)
-//		cmd.add("taskset");
-//		cmd.add("-c");
-//		cmd.add("0,1");
+		ArrayList<String> cmd = new ArrayList<>();
+		String tasksetConfig = videoModule.getTranscodingTasksetConfig();
+		if (tasksetConfig != null) {
+			cmd.add("taskset");
+			cmd.add("-c");
+			cmd.add(tasksetConfig);			
+		}
 		cmd.add("HandBrakeCLI");
 		cmd.add("-i"); 
 		cmd.add(masterFile.getAbsolutePath());
@@ -91,7 +92,7 @@ public class VideoTranscodingTask implements LongRunnable, TaskAwareRunnable, Se
 		cmd.add("--preset");
 		cmd.add("Normal");
 		cmd.add("--height");
-		cmd.add(resolution);
+		cmd.add(Integer.toString(version.getResolution()));
 		cmd.add("--deinterlace");
 		cmd.add("--crop");
 		cmd.add("0:0:0:0");
@@ -110,11 +111,15 @@ public class VideoTranscodingTask implements LongRunnable, TaskAwareRunnable, Se
 				process.destroy();
 				process = null;
 			}
-			//TODO: remove version file, cleanup
+			//TODO: remove version file, cleanup, remove job
 		}
 	}
 
-	
+
+	/**
+	 * Internal helper to deal with the handbrake console output and update the transcoding metadata
+	 * @param proc
+	 */
 	private final void executeProcess(Process proc) {
 		VideoManager videoManager = CoreSpringFactory.getImpl(VideoManager.class);
 		
@@ -122,6 +127,7 @@ public class VideoTranscodingTask implements LongRunnable, TaskAwareRunnable, Se
 		StringBuilder output = new StringBuilder();
 		String line;
 		
+		// Read from standard input and parse percentages of transcoding process
 		InputStream stdout = proc.getInputStream();
 		InputStreamReader isr = new InputStreamReader(stdout);
 		BufferedReader br = new BufferedReader(isr);
@@ -148,7 +154,9 @@ public class VideoTranscodingTask implements LongRunnable, TaskAwareRunnable, Se
 			//
 		}
 
-		InputStream stderr = proc.getErrorStream();
+		// Read and ignore errors, Handbrake outputs a lot info on startup. Only
+		// display errors in debug level
+ 		InputStream stderr = proc.getErrorStream();
 		InputStreamReader iserr = new InputStreamReader(stderr);
 		BufferedReader berr = new BufferedReader(iserr);
 		line = null;
@@ -162,22 +170,19 @@ public class VideoTranscodingTask implements LongRunnable, TaskAwareRunnable, Se
 		}
 
 		try {
+			// On finish, update metadata file
 			int exitValue = proc.waitFor();
 			if (exitValue == 0) {
-				// done, update metadata file
 				MovieService movieService = CoreSpringFactory.getImpl(MovieService.class);
 				Size videoSize = movieService.getSize(new LocalFileImpl(transcodedFile), VideoManagerImpl.FILETYPE_MP4);
 				version.setDimension(videoSize);
 				version.setFileSize(Formatter.formatBytes(transcodedFile.length()));
-				version.setIsTransforming(false);
-				version.setTranscodingStatus(100);
+				version.setTranscodingStatus(VideoQualityVersion.TRANSCODING_STATUS_DONE);
 				videoManager.updateVersion(video, version);
-				
-				//TODO: do I need to remove task from DB?
-
 			}
 		} catch (InterruptedException e) {
 			//
+			//TODO: do I need to remove task from DB?
 		}
 	}
 }
diff --git a/src/main/java/org/olat/modules/video/model/VideoQualityVersion.java b/src/main/java/org/olat/modules/video/model/VideoQualityVersion.java
index 8954f2a9f12..3fd5e4e0d45 100644
--- a/src/main/java/org/olat/modules/video/model/VideoQualityVersion.java
+++ b/src/main/java/org/olat/modules/video/model/VideoQualityVersion.java
@@ -22,33 +22,46 @@ package org.olat.modules.video.model;
 import org.olat.core.commons.services.image.Size;
 
 /**
- * Model of quality-versions to save in a seperate xml-file
+ * Model of quality-versions to save in a separate xml-file
  * 
  * @author Dirk Furrer, dirk.furrer@frentix.com, http://www.frentix.com
  *
  */
-public class VideoQualityVersion{
+public class VideoQualityVersion {
+	public static final int TRANSCODING_STATUS_WAITING = 0;
+	public static final int TRANSCODING_STATUS_DONE = 100;
+
 	// Properties
-	private String type;
+	private int resolution;
 	private String fileSize;
 	private Size dimension;
 	private String format;
-	private boolean isTransforming;
 	private int transcodingStatus = 0;
-	
-	public VideoQualityVersion(String type, String fileSize, Size dimension, String format){
-		this.type = type;
+
+	public VideoQualityVersion(int resolution, String fileSize, Size dimension, String format) {
+		this.resolution = resolution;
 		this.fileSize = fileSize;
 		this.dimension = dimension;
 		this.format = format;
 	}
-	
-	public String getType() {
-		return type;
+
+	/**
+	 * The resolution of the transcoded video. The resolution if defined using
+	 * the video height. E.g. a 1080p video has a resolution of 1080
+	 * 
+	 * @return The height of the transcoded video
+	 */
+	public int getResolution() {
+		return resolution;
 	}
 
-	public void setType(String type) {
-		this.type = type;
+	/**
+	 * @param resolution
+	 *            The resolution if defined using the video height. E.g. a 1080p
+	 *            video has a resolution of 1080
+	 */
+	public void setResolution(int resolution) {
+		this.resolution = resolution;
 	}
 
 	public String getFileSize() {
@@ -74,15 +87,7 @@ public class VideoQualityVersion{
 	public void setFormat(String format) {
 		this.format = format;
 	}
-	
-	public boolean getIsTransforming() {
-		return isTransforming;
-	}
 
-	public void setIsTransforming(boolean isTranscoding) {
-		this.isTransforming = isTranscoding;
-	}
-	
 	/**
 	 * @return 0: transcoding has not yet startet; 100: transcoding is done
 	 */
@@ -91,10 +96,14 @@ public class VideoQualityVersion{
 	}
 
 	/**
-	 * Set transcoding status in percent
+	 * Set transcoding status in percent (0-100)
+	 * 
 	 * @param status
 	 */
 	public void setTranscodingStatus(int status) {
+		if (status > 100 || status < 0) {
+			status = TRANSCODING_STATUS_DONE;
+		}
 		this.transcodingStatus = status;
 	}
 }
\ No newline at end of file
diff --git a/src/main/java/org/olat/modules/video/ui/QualityTableRow.java b/src/main/java/org/olat/modules/video/ui/QualityTableRow.java
index ba2e1c3a6c7..b908b9f9f5f 100644
--- a/src/main/java/org/olat/modules/video/ui/QualityTableRow.java
+++ b/src/main/java/org/olat/modules/video/ui/QualityTableRow.java
@@ -31,7 +31,7 @@ import org.olat.core.gui.components.form.flexible.elements.FormLink;
  */
 public class QualityTableRow {
 
-	String type;
+	String resolution;
 	String dimension;
 	String size;
 	String format;
@@ -39,8 +39,8 @@ public class QualityTableRow {
 
 	protected FormUIFactory uifactory = FormUIFactory.getInstance();
 
-	public QualityTableRow(String type, String dimension, String size, String format, FormLink viewLink) {
-		this.type = type;
+	public QualityTableRow(String resolution, String dimension, String size, String format, FormLink viewLink) {
+		this.resolution = resolution;
 		this.dimension = dimension;
 		this.size = size;
 		this.format = format;
@@ -49,12 +49,12 @@ public class QualityTableRow {
 
 	}
 
-	public String getType() {
-		return type;
+	public String getResolution() {
+		return resolution;
 	}
 
 	public void setType(String type) {
-		this.type = type;
+		this.resolution = type;
 	}
 
 	public String getDimension() {
diff --git a/src/main/java/org/olat/modules/video/ui/VideoQualityTableFormController.java b/src/main/java/org/olat/modules/video/ui/VideoQualityTableFormController.java
index b188a3ded1c..e64bdd9462b 100644
--- a/src/main/java/org/olat/modules/video/ui/VideoQualityTableFormController.java
+++ b/src/main/java/org/olat/modules/video/ui/VideoQualityTableFormController.java
@@ -80,7 +80,7 @@ public class VideoQualityTableFormController extends FormBasicController {
 		formLayout.add(generalCont);
 
 		FlexiTableColumnModel columnsModel = FlexiTableDataModelFactory.createFlexiTableColumnModel();
-		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(true, QualityTableCols.type.i18nKey(), QualityTableCols.type.ordinal(), true, QualityTableCols.type.name()));
+		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(true, QualityTableCols.resolution.i18nKey(), QualityTableCols.resolution.ordinal(), true, QualityTableCols.resolution.name()));
 		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(true, QualityTableCols.dimension.i18nKey(), QualityTableCols.dimension.ordinal(), true, QualityTableCols.dimension.name()));
 		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(true, QualityTableCols.size.i18nKey(), QualityTableCols.size.ordinal(), true, QualityTableCols.size.name()));
 		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(true, QualityTableCols.format.i18nKey(), QualityTableCols.format.ordinal(), true, QualityTableCols.format.name()));
@@ -92,11 +92,11 @@ public class VideoQualityTableFormController extends FormBasicController {
 		Size origSize = videoManager.getVideoSize(videoResource);
 		
 		viewButton = uifactory.addFormLink("view", "viewQuality", "quality.view", "qulaity.view", null, Link.LINK);
-		rows.add(new QualityTableRow("original", origSize.getWidth() +"x"+ origSize.getHeight(),  FileUtils.byteCountToDisplaySize(videoManager.getVideoFile(videoResource).length()), "mp4",viewButton));
+		rows.add(new QualityTableRow(translate("quality.resolution.original"), origSize.getWidth() +"x"+ origSize.getHeight(),  FileUtils.byteCountToDisplaySize(videoManager.getVideoFile(videoResource).length()), "mp4",viewButton));
 		
 		List<VideoQualityVersion> versions = videoManager.getQualityVersions(videoResource);
 		for(VideoQualityVersion version:versions){
-			viewButton = uifactory.addFormLink(version.getType(), "viewQuality", "quality.view", "qulaity.view", null, Link.LINK);
+			viewButton = uifactory.addFormLink(Integer.toString(version.getResolution()), "viewQuality", "quality.view", "qulaity.view", null, Link.LINK);
 			Size size = version.getDimension();
 			String dimension = "";
 			if (size != null) {
@@ -105,15 +105,12 @@ public class VideoQualityTableFormController extends FormBasicController {
 			String fileSize = "";
 			if (version.getFileSize() != null) {
 				fileSize = version.getFileSize();
-			} else if (version.getIsTransforming()) {
-				// TODO refactor to separate column
-				if (version.getTranscodingStatus() == 0) {
-					fileSize = translate("transcoding.waiting");
-				} else {
-					fileSize = translate("transcoding.processing") + ": " + version.getTranscodingStatus() + "%";					
-				}
+			} else if (version.getTranscodingStatus() == VideoQualityVersion.TRANSCODING_STATUS_WAITING) {
+				fileSize = translate("transcoding.waiting");
+			} else if (version.getTranscodingStatus() == VideoQualityVersion.TRANSCODING_STATUS_DONE){
+				fileSize = translate("transcoding.processing") + ": " + version.getTranscodingStatus() + "%";					
 			}
-			rows.add(new QualityTableRow(translate("quality.type." + version.getType()), dimension,  fileSize, version.getFormat(),viewButton));
+			rows.add(new QualityTableRow(translate("quality.resolution." + version.getResolution()), dimension,  fileSize, version.getFormat(),viewButton));
 		}
 		
 		tableModel.setObjects(rows);
diff --git a/src/main/java/org/olat/modules/video/ui/VideoQualityTableModel.java b/src/main/java/org/olat/modules/video/ui/VideoQualityTableModel.java
index 6740afed860..e4c2bac78fc 100644
--- a/src/main/java/org/olat/modules/video/ui/VideoQualityTableModel.java
+++ b/src/main/java/org/olat/modules/video/ui/VideoQualityTableModel.java
@@ -50,7 +50,7 @@ public class VideoQualityTableModel extends DefaultFlexiTableDataModel<QualityTa
 	public Object getValueAt(int row, int col) {
 		QualityTableRow video = getObject(row);
 		switch(QualityTableCols.values()[col]) {
-			case type: return video.getType();
+			case resolution: return video.getResolution();
 			case dimension: return video.getDimension();
 			case size: return video.getSize();
 			case format: return video.getFormat();
@@ -60,7 +60,7 @@ public class VideoQualityTableModel extends DefaultFlexiTableDataModel<QualityTa
 	}
 
 	public enum QualityTableCols {
-		type("quality.table.header.type"),
+		resolution("quality.table.header.resolution"),
 		dimension("quality.table.header.dimension"),
 		size("quality.table.header.size"),
 		format("quality.table.header.format"),
diff --git a/src/main/java/org/olat/modules/video/ui/_content/video_run.html b/src/main/java/org/olat/modules/video/ui/_content/video_run.html
index c476a195883..7ef5ccf36a2 100644
--- a/src/main/java/org/olat/modules/video/ui/_content/video_run.html
+++ b/src/main/java/org/olat/modules/video/ui/_content/video_run.html
@@ -1,10 +1,11 @@
 <div class="o_video_run o_clearfix">
 	<div class="olatFlashMovieViewer">
 		<video id="$r.getId("o_vid")" width="$width" height="$height" poster="$mediaUrl/poster.jpg" controls  #if( $autoplay ) autoplay #end class="o_video">			
+   			#set( $hasOptimizedVideo = false)
 	    	#foreach( $video in $videos)
-	    		#if(!$video.getIsTransforming())
+	    		#if($video.getTranscodingStatus() == 100)
 	    			#set( $hasOptimizedVideo = true)
-		    		<source type="video/mp4" src="$mediaUrl/optimizedVideoData/${video.getType()}video.mp4" title="$r.translate("quality.type.${video.getType()}") (${video.getFileSize()})"/>
+		    		<source type="video/mp4" src="$mediaUrl/optimizedVideoData/${video.getResolution()}video.mp4" title="$r.translate("quality.type.${video.getType()}") (${video.getFileSize()})"/>
 	    		#end
 	    	#end	    	
 	    	## Use master video file if not optimized video is found
diff --git a/src/main/java/org/olat/modules/video/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/modules/video/ui/_i18n/LocalStrings_de.properties
index f1b29ad12c3..b25705f6b96 100644
--- a/src/main/java/org/olat/modules/video/ui/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/modules/video/ui/_i18n/LocalStrings_de.properties
@@ -10,15 +10,14 @@ poster.select=Dieses Vorschaubild ausw\u00E4hlen
 quality.table.header.dimension=Dimension
 quality.table.header.format=Format
 quality.table.header.size=Gr\u00F6sse
-quality.table.header.type=Typ
+quality.table.header.resolution=Auflösung
 quality.table.header.view=ansehen
 quality.view=vorschau
-quality.type.1080=1080p Full-HD
-quality.type.720=720p HD
-quality.type.480=480p
-quality.type.360=360p
-quality.type.240=240p
-quality.type.144=144p
+quality.resolution.original=Master Video
+quality.resolution.1080=1080p Full-HD
+quality.resolution.720=720p HD
+quality.resolution.480=480p
+quality.resolution.360=360p
 transcoding.waiting=In Warteschlange
 transcoding.processing=In Bearbeitung
 tab.video.metaDataConfig=Metadaten konfigurieren
diff --git a/src/main/java/org/olat/modules/video/ui/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/modules/video/ui/_i18n/LocalStrings_en.properties
index 3e4d48f99c7..35db5621258 100644
--- a/src/main/java/org/olat/modules/video/ui/_i18n/LocalStrings_en.properties
+++ b/src/main/java/org/olat/modules/video/ui/_i18n/LocalStrings_en.properties
@@ -11,6 +11,7 @@ tab.video.metaDataConfig=Metadata configuration
 tab.video.posterConfig=Poster configuration
 tab.video.settings=Video settings
 tab.video.trackConfig=Subtitle configuration
+tab.video.qualityConfig=Video quality
 table.header.delete=Delete
 table.header.file=File
 table.header.language=Language
@@ -37,11 +38,16 @@ video.config.tracks.table.add=add
 video.config.tracks.table.delete=delete
 video.config.tracks.table.lang=language
 video.config.width=Width
-quality.type.1080=1080p Full-HD
-quality.type.720=720p HD
-quality.type.480=480p
-quality.type.360=360p
-quality.type.240=240p
-quality.type.144=144p
+quality.resolution.original=Master video
+quality.resolution.1080=1080p Full-HD
+quality.resolution.720=720p HD
+quality.resolution.480=480p
+quality.resolution.360=360p
+quality.table.header.dimension=Dimension
+quality.table.header.format=Format
+quality.table.header.size=Size
+quality.table.header.resolution=Resolution
+quality.table.header.view=Preview
+quality.view=preview
 transcoding.waiting=Queuing
 transcoding.processing=Processing
diff --git a/src/main/resources/serviceconfig/olat.properties b/src/main/resources/serviceconfig/olat.properties
index 8daea69b6c0..3b08ae3dff1 100644
--- a/src/main/resources/serviceconfig/olat.properties
+++ b/src/main/resources/serviceconfig/olat.properties
@@ -1157,7 +1157,6 @@ vc.openmeetings.customerid=1
 vc.gotomeetings.enabled=false
 vc.gotomeetings.consumerKey=
 
-
 ########################################
 # Options for monitoring
 ########################################
@@ -1166,6 +1165,14 @@ monitoring.instance.description=OpenOLAT instance
 monitored.probes=Runtime,System,Database,Memory,OpenOLAT,Release,Environment,Indexer
 monitoring.dependency.server=myserver
 
+# Video resource
+video.enabled=true
+video.coursenode.enabled=true
+# The binary "HandBrakeCLI" must be installed to make transcoding work
+video.transcoding.enabled=false
+video.transcoding.resolutions=1080,720,480,360
+# Use tasklist to limit CPU usage.
+video.transcoding.taskset.cpuconfig=0,1
+video.transcoding.taskset.cpuconfig.values=0,1 to indicate usage of 2 cores, set empty value to disable taskset (e.g. on osx not available)
+
 
-#
-video.transcoding.provider=handbrake
-- 
GitLab