From d13527ced549bdea2653ae44c83133f4d306fd4e Mon Sep 17 00:00:00 2001
From: fkiefer <none@none>
Date: Thu, 2 Mar 2017 14:19:53 +0100
Subject: [PATCH] OO-2595	 Add filter to avoid black video poster
 suggestions in video/poster config

---
 .../org/olat/modules/video/VideoManager.java  | 11 ++++
 .../video/manager/VideoManagerImpl.java       | 55 +++++++++++++++++++
 .../video/ui/VideoPosterSelectionForm.java    | 29 ++++++++--
 3 files changed, 89 insertions(+), 6 deletions(-)

diff --git a/src/main/java/org/olat/modules/video/VideoManager.java b/src/main/java/org/olat/modules/video/VideoManager.java
index 307cebdaa60..1dd9cf67315 100644
--- a/src/main/java/org/olat/modules/video/VideoManager.java
+++ b/src/main/java/org/olat/modules/video/VideoManager.java
@@ -463,4 +463,15 @@ public interface VideoManager {
 	 */
 	public boolean hasVideoMetadata(OLATResource videoResource);
 
+	/**
+ 	 * get Frame at given frameNumber in video and save it in the VFSLeaf 'frame' if the image is not mostly black
+ 	 *
+	 * @param videoResource
+	 * @param frameNumber
+	 * @param duration
+	 * @param frame resource
+	 * @return true if image proposal is mostly black
+	 */
+	public abstract boolean getFrameWithFilter(OLATResource videoResource, int frameNumber, long duration, VFSLeaf frame);
+
 }
\ No newline at end of file
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 4599de7e460..f1eef026185 100644
--- a/src/main/java/org/olat/modules/video/manager/VideoManagerImpl.java
+++ b/src/main/java/org/olat/modules/video/manager/VideoManagerImpl.java
@@ -282,6 +282,61 @@ public class VideoManagerImpl implements VideoManager {
 			return false;
 		} 
 	}
+	
+	@Override
+	public boolean getFrameWithFilter(OLATResource videoResource, int frameNumber, long duration, VFSLeaf frame) {
+		File videoFile = ((LocalFileImpl) getMasterVideoFile(videoResource)).getBasefile();
+		BufferedImage bufImg = null;
+		boolean imgBlack = true;
+		int countBlack = 0;
+		try (RandomAccessFile randomAccessFile = new RandomAccessFile(videoFile, "r")) {
+			OutputStream frameOutputStream = frame.getOutputStream(false);
+			
+			FileChannel ch = randomAccessFile.getChannel();
+			FileChannelWrapper in = new FileChannelWrapper(ch);
+			FrameGrab frameGrab = new FrameGrab(in).seekToFrameSloppy(frameNumber);
+
+			bufImg = frameGrab.getFrame();
+
+			int xmin = bufImg.getMinX();
+			int ymin = bufImg.getMinY();
+			int xmax = xmin + bufImg.getWidth();
+			int ymax = ymin + bufImg.getHeight();
+			int pixelCount = bufImg.getWidth() * bufImg.getHeight();
+
+			for (int x = xmin; x < xmax; x++) {
+				for (int y = ymin; y < ymax; y++) {
+					int rgb = bufImg.getRGB(x, y);
+//					int alpha = (0xff000000 & rgb) >>> 24;
+					int r = (0x00ff0000 & rgb) >> 16;
+					int g = (0x0000ff00 & rgb) >> 8;
+					int b = (0x000000ff & rgb);
+					if (r < 30 && g < 30 && b < 30) {
+						countBlack++;
+					}
+				}
+			}
+			if (countBlack > (int) (0.7F * pixelCount)) {
+				imgBlack = true;
+			} else {
+				imgBlack = false;
+				ImageIO.write(bufImg, "JPG", frameOutputStream);
+			}
+			// avoid endless loop
+			if (frameNumber > duration) {
+				imgBlack = false;
+			} 
+			// close everything to prevent resource leaks
+			frameOutputStream.close();
+			in.close();
+			ch.close();
+
+			return imgBlack;
+		} catch (Exception | AssertionError e) {
+			log.error("Could not get frame::" + frameNumber + " for video::" + videoFile.getAbsolutePath(), e);
+			return false;
+		}
+	}
 
 	/**
 	 * get the File of the videoresource 
diff --git a/src/main/java/org/olat/modules/video/ui/VideoPosterSelectionForm.java b/src/main/java/org/olat/modules/video/ui/VideoPosterSelectionForm.java
index 2d1af01e0b9..ecccc5fd634 100644
--- a/src/main/java/org/olat/modules/video/ui/VideoPosterSelectionForm.java
+++ b/src/main/java/org/olat/modules/video/ui/VideoPosterSelectionForm.java
@@ -87,22 +87,39 @@ public class VideoPosterSelectionForm extends BasicController {
 		}
 
 		long firstThirdDuration = duration/7;
-		for (int x = 0; x <= duration; x += firstThirdDuration) {
+		for (int currentFrame = 0; currentFrame <= duration; currentFrame += firstThirdDuration) {
 			try {
-				String fileName = FILENAME_PREFIX_PROPOSAL_POSTER + x + FILENAME_POSTFIX_JPG;
-				VFSLeaf posterProposal = tmpContainer.createChildLeaf(fileName);
-				videoManager.getFrame(videoResource, x, posterProposal);
+				String fileName;
+				boolean imgBlack;
+				int adjust = 0;
+				do {
+					fileName = FILENAME_PREFIX_PROPOSAL_POSTER + (currentFrame+adjust) + FILENAME_POSTFIX_JPG;
+					VFSLeaf posterProposal = tmpContainer.createChildLeaf(fileName);
+					imgBlack = videoManager.getFrameWithFilter(videoResource, (currentFrame+adjust), duration, posterProposal);
+					int step = 24;
+					if (currentFrame + step <= duration) {
+						adjust += step;
+					} else {
+						adjust -= step;
+					}
+					// set lower bound to avoid endless loop
+					if (currentFrame+adjust < 0) {
+						// if all poster images are mostly black just take current frame
+						videoManager.getFrame(videoResource, currentFrame, posterProposal);
+						break;
+					}
+				} while (imgBlack);
 				VideoMediaMapper mediaMapper = new VideoMediaMapper(tmpContainer);
 				String mediaUrl = registerMapper(ureq, mediaMapper);
 				String serverUrl = Settings.createServerURI();
 				proposalLayout.contextPut("serverUrl", serverUrl);
 
-				Link button = LinkFactory.createButton(String.valueOf(x), proposalLayout, this);
+				Link button = LinkFactory.createButton(String.valueOf(currentFrame), proposalLayout, this);
 				button.setCustomEnabledLinkCSS("o_video_poster_selct");
 				button.setCustomDisplayText(translate("poster.select"));
 				button.setUserObject(fileName);
 				
-				generatedPosters.put(mediaUrl + "/" + fileName,	String.valueOf(x));
+				generatedPosters.put(mediaUrl + "/" + fileName,	String.valueOf(currentFrame));
 			} catch (Exception | AssertionError e) {
 				logError("Error while creating poster images for video::" + videoFile.getAbsolutePath(), e);
 			}
-- 
GitLab