diff --git a/src/main/java/org/olat/modules/video/VideoManager.java b/src/main/java/org/olat/modules/video/VideoManager.java
index 48ce846a43a654aefc3567423ceaf4f9af3ee88f..722889a182cddb7ef2a1c8ee8aca55b9a0c70f74 100644
--- a/src/main/java/org/olat/modules/video/VideoManager.java
+++ b/src/main/java/org/olat/modules/video/VideoManager.java
@@ -72,6 +72,13 @@ public interface VideoManager {
 	 */
 	public abstract void startTranscodingProcess(OLATResource video);
 	
+	/**
+	 * Get all video transcodings for a specific video resource, sorted by
+	 * resolution, highes resolution first
+	 * 
+	 * @param video
+	 * @return
+	 */
 	public abstract List<VideoTranscoding> getVideoTranscodings(OLATResource video);
 
 	/**
@@ -177,4 +184,9 @@ public interface VideoManager {
 	 */
 	public abstract boolean deleteVideoTranscodings(OLATResource videoResource);
 
+	/**
+	 * @return List of video transcodings which have not yet been done
+	 */
+	public abstract List<VideoTranscoding> getVideoTranscodingsPendingAndInProgress();
+
 }
\ No newline at end of file
diff --git a/src/main/java/org/olat/modules/video/VideoModule.java b/src/main/java/org/olat/modules/video/VideoModule.java
index e7a86b2bad400ba5555d44bb1b527be9017cd41a..709fbcc0ccac964a823f464324fa8c96b0c1f4d9 100644
--- a/src/main/java/org/olat/modules/video/VideoModule.java
+++ b/src/main/java/org/olat/modules/video/VideoModule.java
@@ -48,6 +48,8 @@ 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";
+	private static final String VIDEOTRANSCODING_LOCAL = "video.transcoding.local";
+
 
 	@Value("${video.enabled:true}")
 	private boolean enabled;
@@ -56,6 +58,8 @@ public class VideoModule extends AbstractSpringModule {
 	// transcoding related configuration
 	@Value("${video.transcoding.enabled:false}")
 	private boolean transcodingEnabled;
+	@Value("${video.transcoding.local:true}")
+	private boolean transcodingLocal;
 	@Value("${video.transcoding.resolutions}")
 	private String transcodingResolutions;
 	@Value("${video.transcoding.taskset.cpuconfig}")
@@ -65,6 +69,7 @@ public class VideoModule extends AbstractSpringModule {
 	
 	private int[] transcodingResolutionsArr = new int[] { 1080,720,480,360 };
 
+
 	@Autowired
 	public VideoModule(CoordinatorManager coordinatorManager) {
 		super(coordinatorManager);
@@ -104,11 +109,17 @@ public class VideoModule extends AbstractSpringModule {
 			transcodingEnabled = "true".equals(enabledTranscodingObj);
 		}
 
+		String localTranscodingObj = getStringPropertyValue(VIDEOTRANSCODING_LOCAL, true);
+		if(StringHelper.containsNonWhitespace(localTranscodingObj)) {
+			transcodingLocal = "true".equals(localTranscodingObj);
+		}
+
 		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());
+		log.info("video.transcoding.local=" + isTranscodingLocal());
 	}
 
 	/**
@@ -120,6 +131,7 @@ public class VideoModule extends AbstractSpringModule {
 	 */
 	public int[] getTranscodingResolutions() {
 		return transcodingResolutionsArr;
+		//TODO: implement GUI for reading/Setting
 	}
 
 	/**
@@ -192,4 +204,15 @@ public class VideoModule extends AbstractSpringModule {
 		setStringProperty(VIDEOTRANSCODING_ENABLED, Boolean.toString(transcodingEnabled), true);
 		//TODO: check all video resources if there are missing versions
 	}
+
+	public boolean isTranscodingLocal() {		
+		return isTranscodingEnabled() && transcodingLocal;
+		//TODO: implement GUI for reading/Setting
+	}
+
+	public void setTranscoding(boolean transcodingLocal) {
+		this.transcodingLocal = transcodingLocal;
+		setStringProperty(VIDEOTRANSCODING_LOCAL, Boolean.toString(transcodingLocal), true);
+	}
+
 }
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 0e159fd4c9c184feacb56eab515afc69ed55b7b7..61decc02d22093e860613ab8c2f42c542516cb51 100644
--- a/src/main/java/org/olat/modules/video/manager/VideoManagerImpl.java
+++ b/src/main/java/org/olat/modules/video/manager/VideoManagerImpl.java
@@ -28,7 +28,6 @@ import java.io.RandomAccessFile;
 import java.math.RoundingMode;
 import java.nio.channels.FileChannel;
 import java.text.DecimalFormat;
-import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map.Entry;
@@ -42,7 +41,6 @@ import org.jcodec.api.FrameGrab;
 import org.jcodec.common.FileChannelWrapper;
 import org.olat.core.commons.modules.bc.vfs.OlatRootFolderImpl;
 import org.olat.core.commons.services.image.Size;
-import org.olat.core.commons.services.taskexecutor.TaskExecutorManager;
 import org.olat.core.commons.services.video.MovieService;
 import org.olat.core.gui.translator.Translator;
 import org.olat.core.logging.OLog;
@@ -70,6 +68,9 @@ import org.olat.repository.RepositoryEntryImportExport;
 import org.olat.repository.RepositoryEntryImportExport.RepositoryEntryImport;
 import org.olat.repository.RepositoryManager;
 import org.olat.resource.OLATResource;
+import org.quartz.JobDetail;
+import org.quartz.Scheduler;
+import org.quartz.SchedulerException;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
@@ -95,9 +96,9 @@ public class VideoManagerImpl implements VideoManager {
 	@Autowired 
 	private RepositoryManager repositoryManager;
 	@Autowired
-	private TaskExecutorManager taskManager;
-	@Autowired
 	private VideoTranscodingDAO videoTranscodingDao;
+	@Autowired
+	private Scheduler scheduler;
 	
 	private static final OLog log = Tracing.createLoggerFor(VideoManagerImpl.class);
 
@@ -250,34 +251,57 @@ public class VideoManagerImpl implements VideoManager {
 	
 	@Override
 	public void startTranscodingProcess(OLATResource video) {
-		//TODO: check for existing version, add option to force rebuild of all versions
-		//TODO: GUI to admin console to manage transcoding resolutions
+		List<VideoTranscoding> existingTranscodings = getVideoTranscodings(video);
 		VideoMetadata videoMetadata = readVideoMetadataFile(video);
 		int height = videoMetadata.getHeight();
 		// 1) setup transcoding job for original file size
-		VideoTranscoding videoTranscoding = videoTranscodingDao.createVideoTranscoding(video, height, VideoTranscoding.FORMAT_MP4);
-		VideoTranscodingTask task = new VideoTranscodingTask(video, videoTranscoding);
-		taskManager.execute(task, null, video, null, new Date());		
+		createTranscodingIfNotCreatedAlready(video, height, VideoTranscoding.FORMAT_MP4, existingTranscodings);
 		// 2) setup transcoding jobs for all configured sizes below the original size
 		int[] resolutions = videoModule.getTranscodingResolutions();
 		for (int resolution : resolutions) {
 			if (height <= resolution) {
 				continue;
 			}
-			videoTranscoding = videoTranscodingDao.createVideoTranscoding(video, resolution, VideoTranscoding.FORMAT_MP4);
-			task = new VideoTranscodingTask(video, videoTranscoding);
-			taskManager.execute(task, null, video, null, new Date());		
+			createTranscodingIfNotCreatedAlready(video, resolution, VideoTranscoding.FORMAT_MP4, existingTranscodings);
+		}
+		// 3) Start transcoding immediately, force job execution
+		if (videoModule.isTranscodingLocal()) {
+			try {
+				JobDetail detail = scheduler.getJobDetail("videoTranscodingJobDetail", Scheduler.DEFAULT_GROUP);
+				scheduler.triggerJob(detail.getName(), detail.getGroup());
+			} catch (SchedulerException e) {
+				log.error("Error while starting video transcoding job", e);
+			}			
 		}
-		// start transcoding immediately
-		taskManager.executeTaskToDo();
 	}
 	
+	/**
+	 * Helper to check if a transcoding already exists and only create if not
+	 * @param video
+	 * @param resolution
+	 * @param format
+	 * @param existingTranscodings
+	 */
+	private void createTranscodingIfNotCreatedAlready(OLATResource video, int resolution, String format, List<VideoTranscoding> existingTranscodings) {
+		boolean found = false;
+		for (VideoTranscoding videoTranscoding : existingTranscodings) {
+			if (videoTranscoding.getResolution() == resolution) {
+				found = true;
+				break;
+			}
+		}
+		if (!found) {
+			videoTranscodingDao.createVideoTranscoding(video, resolution, format);
+		}		
+	}
+
 	
 	@Override
 	public List<VideoTranscoding> getVideoTranscodings(OLATResource video){
 		List<VideoTranscoding> videoTranscodings = videoTranscodingDao.getVideoTranscodings(video);
 		return videoTranscodings;
 	}
+	
 
 	@Override
 	public String getAspectRatio(int width, int height) {
@@ -433,8 +457,13 @@ public class VideoManagerImpl implements VideoManager {
 		VideoMetadata metaData = new VideoMetadataImpl();
 		// calculate video size
 		Size videoSize = movieService.getSize(targetFile, FILETYPE_MP4);
-		metaData.setWidth(videoSize.getWidth());
-		metaData.setHeight(videoSize.getHeight());
+		if (videoSize != null) {
+			metaData.setWidth(videoSize.getWidth());
+			metaData.setHeight(videoSize.getHeight());			
+		} else {
+			metaData.setWidth(600);
+			metaData.setHeight(800);						
+		}
 		// generate a poster image, use 20th frame as a default
 		VFSLeaf posterResource = VFSManager.resolveOrCreateLeafFromPath(masterContainer, FILENAME_POSTER_JPG);
 		getFrame(videoResource, 20, posterResource);
@@ -510,5 +539,10 @@ public class VideoManagerImpl implements VideoManager {
 		return (deleteStatus == VFSConstants.YES ? true : false);
 	}
 
+	@Override
+	public List<VideoTranscoding> getVideoTranscodingsPendingAndInProgress() {
+		return videoTranscodingDao.getVideoTranscodingsPendingAndInProgress();
+	}
+
 
 }
diff --git a/src/main/java/org/olat/modules/video/manager/VideoTranscodingDAO.java b/src/main/java/org/olat/modules/video/manager/VideoTranscodingDAO.java
index 69fc46a550a0f26d84958b6a08d6bedac540c337..20c407a310e441354f8a62c4dab78d591d3d45c9 100644
--- a/src/main/java/org/olat/modules/video/manager/VideoTranscodingDAO.java
+++ b/src/main/java/org/olat/modules/video/manager/VideoTranscodingDAO.java
@@ -33,6 +33,7 @@ import org.springframework.stereotype.Service;
  * DAO implementation for manipulating VideoTranscoding objects
  * 
  * Initial date: 05.05.2016<br>
+ * 
  * @author gnaegi, gnaegi@frentix.com, http://www.frentix.com
  *
  */
@@ -43,13 +44,15 @@ public class VideoTranscodingDAO {
 	private DB dbInstance;
 
 	/**
-	 * Factory method to create and persist new video transcoding objects for a given video resource
+	 * Factory method to create and persist new video transcoding objects for a
+	 * given video resource
+	 * 
 	 * @param videoResource
 	 * @param resolution
 	 * @param format
 	 * @return
 	 */
-	public VideoTranscoding createVideoTranscoding(OLATResource videoResource, int resolution, String format) {
+	VideoTranscoding createVideoTranscoding(OLATResource videoResource, int resolution, String format) {
 		VideoTranscodingImpl videoTranscoding = new VideoTranscodingImpl();
 		videoTranscoding.setCreationDate(new Date());
 		videoTranscoding.setLastModified(videoTranscoding.getCreationDate());
@@ -60,30 +63,70 @@ public class VideoTranscodingDAO {
 		dbInstance.getCurrentEntityManager().persist(videoTranscoding);
 		return videoTranscoding;
 	}
-	
-	public List<VideoTranscoding> getVideoTranscodings(OLATResource videoResource) {
-		StringBuilder sb = new StringBuilder();
-		sb.append("select trans from videotranscoding as trans")
-		  .append(" inner join fetch trans.videoResource as res")
-		  .append(" where res.key=:resourceKey");
-		return dbInstance.getCurrentEntityManager()
-				.createQuery(sb.toString(), VideoTranscoding.class)
-				.setParameter("resourceKey", videoResource.getKey())
-				.getResultList();
-	}
 
-	public VideoTranscoding updateTranscoding(VideoTranscoding videoTranscoding) {
-		((VideoTranscodingImpl)videoTranscoding).setLastModified(new Date());
+	/**
+	 * Merge updated video transcoding, persist on DB
+	 * 
+	 * @param videoTranscoding
+	 * @return Updated transcoding object
+	 */
+	VideoTranscoding updateTranscoding(VideoTranscoding videoTranscoding) {
+		((VideoTranscodingImpl) videoTranscoding).setLastModified(new Date());
 		VideoTranscoding trans = dbInstance.getCurrentEntityManager().merge(videoTranscoding);
 		return trans;
 	}
 
-	public int deleteVideoTranscodings(OLATResource videoResource) {
+	/**
+	 * Delete all video transcoding objects for a given video resource
+	 * 
+	 * @param videoResource
+	 * @return
+	 */
+	int deleteVideoTranscodings(OLATResource videoResource) {
 		String deleteQuery = "delete from videotranscoding where fk_resource_id=:resourceKey";
-		return dbInstance.getCurrentEntityManager()
-				.createQuery(deleteQuery).setParameter("resourceKey", videoResource.getKey())
-				.executeUpdate();
+		return dbInstance.getCurrentEntityManager().createQuery(deleteQuery)
+				.setParameter("resourceKey", videoResource.getKey()).executeUpdate();
+	}
+
+	/**
+	 * Delete a specifig video transcoding version
+	 * 
+	 * @param videoTranscoding
+	 */
+	void deleteVideoTranscoding(VideoTranscoding videoTranscoding) {
+		dbInstance.getCurrentEntityManager().remove(videoTranscoding);
+	}
+
+	/**
+	 * Get all video transcodings for a specific video resource, sorted by
+	 * resolution, highes resolution first
+	 * 
+	 * @param videoResource
+	 * @return
+	 */
+	List<VideoTranscoding> getVideoTranscodings(OLATResource videoResource) {
+		StringBuilder sb = new StringBuilder();
+		sb.append("select trans from videotranscoding as trans")
+			.append(" inner join fetch trans.videoResource as res")
+			.append(" where res.key=:resourceKey")
+			.append(" order by trans.resolution desc");
+		return dbInstance.getCurrentEntityManager().createQuery(sb.toString(), VideoTranscoding.class)
+				.setParameter("resourceKey", videoResource.getKey()).getResultList();
+	}
+
+	/**
+	 * Get all video transcodings which are waiting for transcoding or are
+	 * currently in transcoding in FIFO ordering
+	 * 
+	 * @return
+	 */
+	List<VideoTranscoding> getVideoTranscodingsPendingAndInProgress() {
+		StringBuilder sb = new StringBuilder();
+			sb.append("select trans from videotranscoding as trans")
+			.append(" inner join fetch trans.videoResource as res")
+			.append(" where trans.status != 100")
+			.append(" order by trans.creationDate asc, trans.id asc");
+		return dbInstance.getCurrentEntityManager().createQuery(sb.toString(), VideoTranscoding.class).getResultList();
 	}
 
-	
 }
diff --git a/src/main/java/org/olat/modules/video/manager/VideoTranscodingJob.java b/src/main/java/org/olat/modules/video/manager/VideoTranscodingJob.java
index a24b9628adf94c160f07b1b71252042894da83e5..baea8285cd38cba634ca3a399a8c9ded1fcb1312 100644
--- a/src/main/java/org/olat/modules/video/manager/VideoTranscodingJob.java
+++ b/src/main/java/org/olat/modules/video/manager/VideoTranscodingJob.java
@@ -19,9 +19,30 @@
  */
 package org.olat.modules.video.manager;
 
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.olat.core.CoreSpringFactory;
+import org.olat.core.commons.persistence.DBFactory;
+import org.olat.core.commons.services.image.Size;
 import org.olat.core.commons.services.scheduler.JobWithDB;
+import org.olat.core.commons.services.video.MovieService;
+import org.olat.core.logging.OLog;
+import org.olat.core.logging.Tracing;
+import org.olat.core.util.vfs.LocalFileImpl;
+import org.olat.core.util.vfs.LocalFolderImpl;
+import org.olat.modules.video.VideoManager;
+import org.olat.modules.video.VideoModule;
+import org.olat.modules.video.VideoTranscoding;
+import org.olat.resource.OLATResource;
 import org.quartz.JobExecutionContext;
 import org.quartz.JobExecutionException;
+import org.quartz.StatefulJob;
 
 /**
  * 
@@ -29,15 +50,200 @@ import org.quartz.JobExecutionException;
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  *
  */
-public class VideoTranscodingJob extends JobWithDB {
+public class VideoTranscodingJob extends JobWithDB implements StatefulJob {
+	private static final OLog log = Tracing.createLoggerFor(VideoTranscodingJob.class);
 
 	/**
 	 * 
 	 * @see org.olat.core.commons.services.scheduler.JobWithDB#executeWithDB(org.quartz.JobExecutionContext)
 	 */
 	@Override
-	public void executeWithDB(JobExecutionContext arg0) throws JobExecutionException {
-		System.out.println("Work");
+	public void executeWithDB(JobExecutionContext context) throws JobExecutionException {
+		// uses StatefulJob interface to prevent concurrent job execution
+		doExecute(context);
+	}
+
+	/**
+	 * Implementation of job execution
+	 * @param context
+	 * @return
+	 * @throws JobExecutionException
+	 */
+	private boolean doExecute(JobExecutionContext context) throws JobExecutionException {
+		VideoModule videoModule = CoreSpringFactory.getImpl(VideoModule.class);
+		if (!videoModule.isTranscodingLocal()) {
+			log.debug("Skipping execution of video transcoding job, local transcoding disabled");
+			return false;
+		}
+		
+		// Find first one to work with
+		VideoManager videoManager = CoreSpringFactory.getImpl(VideoManager.class);
+		List<VideoTranscoding> videoTranscodings = videoManager.getVideoTranscodingsPendingAndInProgress();
+		VideoTranscoding videoTranscoding = null;
+		for (VideoTranscoding videoTrans : videoTranscodings) {
+			String transcoder = videoTrans.getTranscoder();
+			if (transcoder == null) { 
+				log.info("Start transcoding video with resolution::" + videoTrans.getResolution()
+					+ " for video resource::" + videoTrans.getVideoResource().getResourceableId());
+				videoTrans.setTranscoder(VideoTranscoding.TRANSCODER_LOCAL);
+				videoTranscoding = videoManager.updateVideoTranscoding(videoTrans);				
+				break;
+			} else if (transcoder.equals(VideoTranscoding.TRANSCODER_LOCAL)) {
+				log.info("Continue with transcoding video with resolution::" + videoTrans.getResolution()
+					+ " for video resource::" + videoTrans.getVideoResource().getResourceableId());
+				videoTranscoding = videoTrans;								
+				break;
+			}
+		}
+		
+		if (videoTranscoding == null) {
+			log.debug("Skipping execution of video transcoding job, no pending video transcoding found in database");
+			return false;
+		}
+		// Ready transcode, forke process now
+		boolean success = forkTranscodingProcess(videoTranscoding);
+		
+		// Transcoding done, call execution again until no more videos to be
+		// processed. If an error happend, don't continue to not get into a loop
+		if (success) {
+			success = doExecute(context); 
+		}
+		return success;
+	}
+	
+	/**
+	 * Internal helper to fork a process with handbrake and read the values from the process
+	 * @param videoTranscoding
+	 * @return true: all ok; false: an error happend along the way
+	 */
+	private boolean forkTranscodingProcess(VideoTranscoding videoTranscoding) {
+		OLATResource video = videoTranscoding.getVideoResource();
+		VideoModule videoModule = CoreSpringFactory.getImpl(VideoModule.class);
+		VideoManager videoManager = CoreSpringFactory.getImpl(VideoManager.class);
+		File masterFile = videoManager.getVideoFile(video);
+		File transcodingFolder = ((LocalFolderImpl)videoManager.getTranscodingContainer(video)).getBasefile();
+		File transcodedFile = new File(transcodingFolder,  Integer.toString(videoTranscoding.getResolution()) + masterFile.getName());
+		// mark this as beeing transcoded by this local transcoder
+		videoTranscoding.setTranscoder(VideoTranscoding.TRANSCODER_LOCAL);
+		videoTranscoding = videoManager.updateVideoTranscoding(videoTranscoding);
+		
+		ArrayList<String> cmd = new ArrayList<>();
+		String tasksetConfig = videoModule.getTranscodingTasksetConfig();
+		if (tasksetConfig != null && !"Mac OS X".equals(System.getProperty("os.name"))) {
+			cmd.add("taskset");
+			cmd.add("-c");
+			cmd.add(tasksetConfig);			
+		}
+		cmd.add("HandBrakeCLI");
+		cmd.add("-i"); 
+		cmd.add(masterFile.getAbsolutePath());
+		cmd.add("-o"); 
+		cmd.add(transcodedFile.getAbsolutePath());
+		cmd.add("--optimize");
+		cmd.add("--preset");
+		cmd.add("Normal");
+		cmd.add("--height");
+		cmd.add(Integer.toString(videoTranscoding.getResolution()));
+		cmd.add("--deinterlace");
+		cmd.add("--crop");
+		cmd.add("0:0:0:0");
+		
+		Process process = null;
+		try {
+			if(log.isDebug()) {
+				log.debug(cmd.toString());
+			}
+			ProcessBuilder builder = new ProcessBuilder(cmd);
+			process = builder.start();
+			return updateVideoTranscodingFromProcessOutput(process, videoTranscoding, transcodedFile);
+		} catch (IOException e) {
+			log.error ("Could not spawn convert sub process", e);
+			return false;
+		} finally {
+			if (process != null) {
+				process.destroy();
+				process = null;
+			}			
+		}
 	}
 	
+	
+	/**
+	 * Internal helper to deal with the handbrake console output and update the transcoding metadata
+	 * @param proc
+	 * @param videoTranscoding
+	 * @param transcodedFile
+	 * @return true: everything fine; false: an error happended somewhere
+	 */
+	private final boolean updateVideoTranscodingFromProcessOutput(Process proc, VideoTranscoding videoTranscoding, File transcodedFile) {
+		VideoManager videoManager = CoreSpringFactory.getImpl(VideoManager.class);
+		
+		StringBuilder errors = new StringBuilder();
+		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);
+		line = null;
+		try {
+			while ((line = br.readLine()) != null) {
+				output.append(line);
+				// Parse the percentage. Logline looks like this:
+				// Encoding: task 1 of 1, 85.90 % (307.59 fps, avg 330.35 fps, ETA 00h00m05s)
+				int start = line.indexOf(",");
+				if (start != -1) {
+					line = line.substring(start);
+					int end = line.indexOf(".");
+					if (end != -1 && end < 5) {
+						String percent = line.substring(2, end);
+						log.debug("Output: " + percent);		
+						// update version file for UI
+						videoTranscoding.setStatus(Integer.parseInt(percent));
+						videoTranscoding = videoManager.updateVideoTranscoding(videoTranscoding);
+						DBFactory.getInstance().commitAndCloseSession();
+					}
+				}
+			}
+		} catch (IOException e) {
+			//
+		}
+
+		// 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;
+		try {
+			while ((line = berr.readLine()) != null) {
+				errors.append(line);
+				log.debug("Error: " + line);
+			}
+		} catch (IOException e) {
+			//
+		}
+
+		try {
+			// On finish, update metadata file
+			int exitValue = proc.waitFor();
+			if (exitValue == 0) {
+				MovieService movieService = CoreSpringFactory.getImpl(MovieService.class);
+				Size videoSize = movieService.getSize(new LocalFileImpl(transcodedFile), VideoManagerImpl.FILETYPE_MP4);
+				videoTranscoding.setWidth(videoSize.getWidth());
+				videoTranscoding.setHeight(videoSize.getHeight());
+				videoTranscoding.setSize(transcodedFile.length());
+				videoTranscoding.setStatus(VideoTranscoding.TRANSCODING_STATUS_DONE);
+				videoTranscoding = videoManager.updateVideoTranscoding(videoTranscoding);
+				DBFactory.getInstance().commitAndCloseSession();
+				return true;
+			} 
+			return false;
+		} catch (InterruptedException e) {
+			return false;
+		}
+	}
+
+	
 }
diff --git a/src/main/java/org/olat/modules/video/manager/VideoTranscodingTask.java b/src/main/java/org/olat/modules/video/manager/VideoTranscodingTask.java
deleted file mode 100644
index 737bce37bcd745ecebc51beff37faf045dc7ac6e..0000000000000000000000000000000000000000
--- a/src/main/java/org/olat/modules/video/manager/VideoTranscodingTask.java
+++ /dev/null
@@ -1,191 +0,0 @@
-/**
- * <a href="http://www.openolat.org">
- * OpenOLAT - Online Learning and Training</a><br>
- * <p>
- * Licensed under the Apache License, Version 2.0 (the "License"); <br>
- * you may not use this file except in compliance with the License.<br>
- * You may obtain a copy of the License at the
- * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
- * <p>
- * Unless required by applicable law or agreed to in writing,<br>
- * software distributed under the License is distributed on an "AS IS" BASIS, <br>
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
- * See the License for the specific language governing permissions and <br>
- * limitations under the License.
- * <p>
- * Initial code contributed and copyrighted by<br>
- * frentix GmbH, http://www.frentix.com
- * <p>
- */
-package org.olat.modules.video.manager;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.util.ArrayList;
-
-import org.olat.core.CoreSpringFactory;
-import org.olat.core.commons.persistence.DBFactory;
-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.video.MovieService;
-import org.olat.core.logging.OLog;
-import org.olat.core.logging.Tracing;
-import org.olat.core.util.vfs.LocalFileImpl;
-import org.olat.core.util.vfs.LocalFolderImpl;
-import org.olat.modules.video.VideoManager;
-import org.olat.modules.video.VideoModule;
-import org.olat.modules.video.VideoTranscoding;
-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, Sequential {
-	private static final long serialVersionUID = 2982868860465334552L;
-	private static final OLog log = Tracing.createLoggerFor(VideoTranscodingTask.class);
-	private OLATResource video;
-	private VideoTranscoding videoTranscoding;
-	private File transcodedFile;
-	
-	/**
-	 * 
-	 * @param video
-	 * @param version
-	 */
-	VideoTranscodingTask(OLATResource video, VideoTranscoding videoTranscoding) {
-		this.video = video;
-		this.videoTranscoding = videoTranscoding;
-	}
-
-	
-	@Override
-	public void run() {
-		VideoModule videoModule = CoreSpringFactory.getImpl(VideoModule.class);
-		VideoManager videoManager = CoreSpringFactory.getImpl(VideoManager.class);
-		File masterFile = videoManager.getVideoFile(video);
-		File transcodingFolder = ((LocalFolderImpl)videoManager.getTranscodingContainer(video)).getBasefile();
-		transcodedFile = new File(transcodingFolder,  Integer.toString(videoTranscoding.getResolution()) + masterFile.getName());
-		// mark this as beeing transcoded by this local transcoder
-		videoTranscoding.setTranscoder(VideoTranscoding.TRANSCODER_LOCAL);
-		videoTranscoding = videoManager.updateVideoTranscoding(videoTranscoding);
-		
-		ArrayList<String> cmd = new ArrayList<>();
-		String tasksetConfig = videoModule.getTranscodingTasksetConfig();
-		if (tasksetConfig != null && !"Mac OS X".equals(System.getProperty("os.name"))) {
-			cmd.add("taskset");
-			cmd.add("-c");
-			cmd.add(tasksetConfig);			
-		}
-		cmd.add("HandBrakeCLI");
-		cmd.add("-i"); 
-		cmd.add(masterFile.getAbsolutePath());
-		cmd.add("-o"); 
-		cmd.add(transcodedFile.getAbsolutePath());
-		cmd.add("--optimize");
-		cmd.add("--preset");
-		cmd.add("Normal");
-		cmd.add("--height");
-		cmd.add(Integer.toString(videoTranscoding.getResolution()));
-		cmd.add("--deinterlace");
-		cmd.add("--crop");
-		cmd.add("0:0:0:0");
-		
-		Process process = null;
-		try {
-			if(log.isDebug()) {
-				log.debug(cmd.toString());
-			}
-			ProcessBuilder builder = new ProcessBuilder(cmd);
-			process = builder.start();
-			executeProcess(process);
-		} catch (IOException e) {
-			log.error ("Could not spawn convert sub process", e);
-			if (process != null) {
-				process.destroy();
-				process = null;
-			}
-		}
-	}
-
-
-	/**
-	 * 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);
-		
-		StringBuilder errors = new StringBuilder();
-		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);
-		line = null;
-		try {
-			while ((line = br.readLine()) != null) {
-				output.append(line);
-				// Parse the percentage. Logline looks like this:
-				// Encoding: task 1 of 1, 85.90 % (307.59 fps, avg 330.35 fps, ETA 00h00m05s)
-				int start = line.indexOf(",");
-				if (start != -1) {
-					line = line.substring(start);
-					int end = line.indexOf(".");
-					if (end != -1 && end < 5) {
-						String percent = line.substring(2, end);
-						log.debug("Output: " + percent);		
-						// update version file for UI
-						videoTranscoding.setStatus(Integer.parseInt(percent));
-						videoTranscoding = videoManager.updateVideoTranscoding(videoTranscoding);
-						DBFactory.getInstance().commitAndCloseSession();
-					}
-				}
-			}
-		} catch (IOException e) {
-			//
-		}
-
-		// 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;
-		try {
-			while ((line = berr.readLine()) != null) {
-				errors.append(line);
-				log.debug("Error: " + line);
-			}
-		} catch (IOException e) {
-			//
-		}
-
-		try {
-			// On finish, update metadata file
-			int exitValue = proc.waitFor();
-			if (exitValue == 0) {
-				MovieService movieService = CoreSpringFactory.getImpl(MovieService.class);
-				Size videoSize = movieService.getSize(new LocalFileImpl(transcodedFile), VideoManagerImpl.FILETYPE_MP4);
-				videoTranscoding.setWidth(videoSize.getWidth());
-				videoTranscoding.setHeight(videoSize.getHeight());
-				videoTranscoding.setSize(transcodedFile.length());
-				videoTranscoding.setStatus(VideoTranscoding.TRANSCODING_STATUS_DONE);
-				videoTranscoding = videoManager.updateVideoTranscoding(videoTranscoding);
-				DBFactory.getInstance().commitAndCloseSession();
-			}
-		} catch (InterruptedException e) {
-			//
-			//TODO: do I need to remove task from DB?
-		}
-	}
-}
diff --git a/src/main/resources/serviceconfig/olat.properties b/src/main/resources/serviceconfig/olat.properties
index 4989626c794e603c8d3a0b85526b2081f20a0934..a971e39f3655c7696185de0eb7734060b6918301 100644
--- a/src/main/resources/serviceconfig/olat.properties
+++ b/src/main/resources/serviceconfig/olat.properties
@@ -1165,22 +1165,35 @@ monitoring.instance.description=OpenOLAT instance
 monitored.probes=Runtime,System,Database,Memory,OpenOLAT,Release,Environment,Indexer
 monitoring.dependency.server=myserver
 
-# Video resource
+# Video resource and course node
 video.enabled=true
 video.coursenode.enabled=true
-# The binary "HandBrakeCLI" must be installed to make transcoding work
+# Enable transcoding if you want to create optimized version of your video for delivery. 
 video.transcoding.enabled=false
+# The transcoding process can run locally in s separate process using HandBrake. In that 
+# case, the binary "HandBrakeCLI" must be installed in your system and available to the 
+# java environment of your system. 
+# Alternatively, you can disable local transcoding and implement a shell script that runs
+# on another server which reads from the o_vid_transcoding table to separate transcoding
+# from the main server. For larger installations it is recommendet to run the transcoding
+# on a dedicated server
+video.transcoding.local=true
+# Use tasklist to limit CPU usage if you set video.transcoding.local=true
+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)
+# List of transcoding versions to be generated by the transcoder
 video.transcoding.resolutions=1080,720,480,360
 video.transcoding.resolutions.values=2160,1080,720,480,360,240
 # Where to store transcoded versions. This can be located on another path in case the 
 # transcoding service is working on another physical server or you just want it to use 
 # another (cheap) disk. By default it is also located in the olatdata dir. 
 # The master video files are kept in the olatdata directory, this is only about the 
-# transcoded versions
+# transcoded versions.
+# If you set video.transcoding.local=false and use an external script for transcoding, this
+# other script must have access to this video.transcoding.dir as well to store the transcoded
+# videos. Make sure the access permissions are implemented in a way so that the OpenOLAT process
+# can always delete the files from this video.transcoding.dir directory (when deleting the 
+# master video)
 video.transcoding.dir=${folder.root}/transcodedVideos
 video.transcoding.dir.values=${folder.root}/transcodedVideos, /mount/cheap/disk/transcodedVideos
-# 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)
-
 
diff --git a/src/test/java/org/olat/modules/video/manager/VideoTranscodingDAOTest.java b/src/test/java/org/olat/modules/video/manager/VideoTranscodingDAOTest.java
index 6619a10533cc4b344733615dc20250c82c9d69c3..5d81f4239a8506557faee9ac22930175861fea95 100644
--- a/src/test/java/org/olat/modules/video/manager/VideoTranscodingDAOTest.java
+++ b/src/test/java/org/olat/modules/video/manager/VideoTranscodingDAOTest.java
@@ -46,14 +46,34 @@ public class VideoTranscodingDAOTest extends OlatTestCase {
 	@Test
 	public void createVideoTranscoding() {
 		OLATResource resource = JunitTestHelper.createRandomResource();
+		// pending transcoding
 		VideoTranscoding vTranscoding = videoTranscodingDao.createVideoTranscoding(resource, 1080, "mp4");
 		Assert.assertNotNull(vTranscoding);
 		dbInstance.commitAndCloseSession();
-		
+
+		// done transcoding
+		VideoTranscoding vTranscoding2 = videoTranscodingDao.createVideoTranscoding(resource, 720, "mp4");
+		Assert.assertNotNull(vTranscoding2);
+		vTranscoding2.setStatus(VideoTranscoding.TRANSCODING_STATUS_DONE);
+		vTranscoding2.setTranscoder(VideoTranscoding.TRANSCODER_LOCAL);
+		vTranscoding2 = videoTranscodingDao.updateTranscoding(vTranscoding2);
+		Assert.assertNotNull(vTranscoding2);
+		Assert.assertTrue(vTranscoding2.getStatus() == 100);
+		dbInstance.commitAndCloseSession();
+
+		// check for transcodings of resource
 		List<VideoTranscoding> vTranscodingList = videoTranscodingDao.getVideoTranscodings(resource);
 		Assert.assertNotNull(vTranscodingList);
-		Assert.assertEquals(1, vTranscodingList.size());
+		Assert.assertEquals(2, vTranscodingList.size());
 		Assert.assertEquals(vTranscoding, vTranscodingList.get(0));
+		Assert.assertEquals(vTranscoding2, vTranscodingList.get(1));
+
+		// check for overall pending transcodings
+		List<VideoTranscoding> vTranscodingList2 = videoTranscodingDao.getVideoTranscodingsPendingAndInProgress();
+		Assert.assertNotNull(vTranscodingList2);
+		Assert.assertEquals(1, vTranscodingList2.size());
+		Assert.assertEquals(vTranscoding, vTranscodingList2.get(0));
+		
 	}
 
 }