diff --git a/pom.xml b/pom.xml index 6b0b2463d98feb56a30ba6e65a845268f6e885b9..ec4e9aa078ec8ad70152e4c72e4fe33a89abbf7c 100644 --- a/pom.xml +++ b/pom.xml @@ -1948,7 +1948,12 @@ <dependency> <groupId>org.jcodec</groupId> <artifactId>jcodec</artifactId> - <version>0.1.6-3</version> + <version>0.2.3</version> + </dependency> + <dependency> + <groupId>org.jcodec</groupId> + <artifactId>jcodec-javase</artifactId> + <version>0.2.3</version> </dependency> <dependency> <groupId>org.mnode.ical4j</groupId> diff --git a/src/main/java/org/olat/core/commons/services/video/MovieServiceImpl.java b/src/main/java/org/olat/core/commons/services/video/MovieServiceImpl.java index 08dff59d4300c6f89952e64ff8caa999a842e38b..36d51ec0fc8ac142f606a2bc09ba977bfad230c2 100644 --- a/src/main/java/org/olat/core/commons/services/video/MovieServiceImpl.java +++ b/src/main/java/org/olat/core/commons/services/video/MovieServiceImpl.java @@ -28,17 +28,19 @@ import java.nio.channels.FileChannel; import java.util.ArrayList; import java.util.List; +import org.apache.logging.log4j.Logger; import org.jcodec.api.FrameGrab; -import org.jcodec.common.FileChannelWrapper; +import org.jcodec.common.Codec; +import org.jcodec.common.io.FileChannelWrapper; import org.jcodec.containers.mp4.boxes.MovieBox; import org.jcodec.containers.mp4.demuxer.MP4Demuxer; +import org.jcodec.scale.AWTUtil; import org.olat.core.commons.services.image.Size; import org.olat.core.commons.services.image.spi.ImageHelperImpl; import org.olat.core.commons.services.thumbnail.CannotGenerateThumbnailException; import org.olat.core.commons.services.thumbnail.FinalSize; import org.olat.core.commons.services.thumbnail.ThumbnailSPI; import org.olat.core.commons.services.video.spi.FLVParser; -import org.apache.logging.log4j.Logger; import org.olat.core.logging.Tracing; import org.olat.core.util.FileUtils; import org.olat.core.util.WorkThreadInformations; @@ -59,18 +61,18 @@ public class MovieServiceImpl implements MovieService, ThumbnailSPI { private static final Logger log = Tracing.createLoggerFor(MovieServiceImpl.class); private static final List<String> extensions = new ArrayList<>(); - private static final List<String> fourCCs = new ArrayList<>(); + private static final List<Codec> supportedCodecs = new ArrayList<>(); static { // supported file extensions extensions.add("mp4"); extensions.add("m4v"); extensions.add("mov"); // supported fourCC for H264 codec - fourCCs.add("avc1"); - fourCCs.add("davc"); - fourCCs.add("h264"); - fourCCs.add("x264"); - fourCCs.add("vssh"); + supportedCodecs.add(Codec.codecByFourcc("avc1")); + supportedCodecs.add(Codec.codecByFourcc("davc")); + supportedCodecs.add(Codec.codecByFourcc("h264")); + supportedCodecs.add(Codec.codecByFourcc("x264")); + supportedCodecs.add(Codec.codecByFourcc("vssh")); } @@ -96,9 +98,10 @@ public class MovieServiceImpl implements MovieService, ThumbnailSPI { if(extensions.contains(suffix)) { try(RandomAccessFile accessFile = new RandomAccessFile(file, "r"); FileChannel ch = accessFile.getChannel(); - FileChannelWrapper in = new FileChannelWrapper(ch)) { + FileChannelWrapper in = new FileChannelWrapper(ch); + MP4Demuxer demuxer1 = MP4Demuxer.createMP4Demuxer(in)) { + - MP4Demuxer demuxer1 = new MP4Demuxer(in); org.jcodec.common.model.Size size = demuxer1.getMovie().getDisplaySize(); // Case 1: standard case, get dimension from movie int w = size.getWidth(); @@ -117,7 +120,7 @@ public class MovieServiceImpl implements MovieService, ThumbnailSPI { // flag of the movie. Those mp4 guys are really // secretive folks, did not find any documentation about // this. Best guess. - org.jcodec.common.model.Size size2 = demuxer1.getVideoTrack().getBox().getCodedSize(); + org.jcodec.common.model.Size size2 = demuxer1.getVideoTrack().getMeta().getVideoCodecMeta().getSize(); w = size2.getHeight(); h = size2.getWidth(); } catch(Exception e) { @@ -161,9 +164,9 @@ public class MovieServiceImpl implements MovieService, ThumbnailSPI { if(extensions.contains(suffix)) { try(RandomAccessFile accessFile = new RandomAccessFile(file, "r"); FileChannel ch = accessFile.getChannel(); - FileChannelWrapper in = new FileChannelWrapper(ch)) { - - MP4Demuxer demuxer1 = new MP4Demuxer(in); + FileChannelWrapper in = new FileChannelWrapper(ch); + MP4Demuxer demuxer1 = MP4Demuxer.createMP4Demuxer(in)) { + MovieBox movie = demuxer1.getMovie(); long duration = movie.getDuration(); int timescale = movie.getTimescale(); @@ -196,10 +199,9 @@ public class MovieServiceImpl implements MovieService, ThumbnailSPI { if(extensions.contains(suffix)) { try(RandomAccessFile accessFile = new RandomAccessFile(file, "r"); FileChannel ch = accessFile.getChannel(); - FileChannelWrapper in = new FileChannelWrapper(ch)) { - - MP4Demuxer demuxer1 = new MP4Demuxer(in); - return demuxer1.getVideoTrack().getFrameCount(); + FileChannelWrapper in = new FileChannelWrapper(ch); + MP4Demuxer demuxer1 = MP4Demuxer.createMP4Demuxer(in)) { + return demuxer1.getVideoTrack().getMeta().getTotalFrames(); } catch (Exception | AssertionError e) { log.error("Cannot extract num. of frames of: " + media, e); } @@ -226,12 +228,12 @@ public class MovieServiceImpl implements MovieService, ThumbnailSPI { FileChannel ch = accessFile.getChannel(); FileChannelWrapper in = new FileChannelWrapper(ch)) { - MP4Demuxer demuxer1 = new MP4Demuxer(in); - String fourCC = demuxer1.getVideoTrack().getFourcc(); - if (fourCCs.contains(fourCC.toLowerCase())) { + MP4Demuxer demuxer1 = MP4Demuxer.createMP4Demuxer(in); + Codec codec = demuxer1.getVideoTrack().getMeta().getCodec(); + if (supportedCodecs.contains(codec)) { return true; } - log.info("Movie file::" + fileName + " has correct suffix::" + suffix + " but fourCC::" + fourCC + " not in our list of supported codecs."); + log.info("Movie file::" + fileName + " has correct suffix::" + suffix + " but fourCC::" + codec + " not in our list of supported codecs."); } catch (Exception | Error e) { // anticipated exception, is not an mp4 file } @@ -251,7 +253,7 @@ public class MovieServiceImpl implements MovieService, ThumbnailSPI { File baseFile = ((LocalFileImpl)file).getBasefile(); File scaledImage = ((LocalFileImpl)thumbnailFile).getBasefile(); - BufferedImage frame = FrameGrab.getFrame(baseFile, 20); + BufferedImage frame = AWTUtil.toBufferedImage(FrameGrab.getFrameFromFile(baseFile, 20)); Size scaledSize = ImageHelperImpl.calcScaledSize(frame, maxWidth, maxHeight); if(ImageHelperImpl.writeTo(frame, scaledImage, scaledSize, "jpeg")) { size = new FinalSize(scaledSize.getWidth(), scaledSize.getHeight()); diff --git a/src/main/java/org/olat/course/nodes/livestream/manager/LIveStreamServiceImpl.java b/src/main/java/org/olat/course/nodes/livestream/manager/LIveStreamServiceImpl.java index ff9ec5380504bc9e1a6068f010a1a415be349a0a..e209cc1bb3f39a9d9ef98769053c328ae60e1a90 100644 --- a/src/main/java/org/olat/course/nodes/livestream/manager/LIveStreamServiceImpl.java +++ b/src/main/java/org/olat/course/nodes/livestream/manager/LIveStreamServiceImpl.java @@ -19,6 +19,9 @@ */ package org.olat.course.nodes.livestream.manager; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; @@ -52,15 +55,29 @@ public class LIveStreamServiceImpl implements LiveStreamService { public List<? extends LiveStreamEvent> getRunningEvents(CourseCalendars calendars, int bufferBeforeMin, int bufferAfterMin) { Date now = new Date(); + + Calendar cFromStartOfDay = Calendar.getInstance(); + cFromStartOfDay.setTime(now); + int year = cFromStartOfDay.get(Calendar.YEAR); + int month = cFromStartOfDay.get(Calendar.MONTH); + int day = cFromStartOfDay.get(Calendar.DATE); + cFromStartOfDay.set(year, month, day - 1, 0, 0, 0); + Date fromStartOfDay = cFromStartOfDay.getTime(); + Calendar cFrom = Calendar.getInstance(); cFrom.setTime(now); cFrom.add(Calendar.MINUTE, -bufferAfterMin); Date from = cFrom.getTime(); + Calendar cTo = Calendar.getInstance(); cTo.setTime(now); cTo.add(Calendar.MINUTE, bufferBeforeMin); Date to = cTo.getTime(); - return getLiveStreamEvents(calendars, from, to); + + // Use start of day to get all day events as well. + return getLiveStreamEvents(calendars, fromStartOfDay, to).stream() + .filter(startedFilter(from)) + .collect(Collectors.toList()); } @Override @@ -79,6 +96,12 @@ public class LIveStreamServiceImpl implements LiveStreamService { .filter(notStartedFilter(from)) .collect(Collectors.toList()); } + + private Predicate<LiveStreamEvent> startedFilter(Date from) { + return (LiveStreamEvent e) -> { + return e.getBegin().before(from); + }; + } private Predicate<LiveStreamEvent> notStartedFilter(Date from) { return (LiveStreamEvent e) -> { @@ -105,6 +128,7 @@ public class LIveStreamServiceImpl implements LiveStreamService { } } } + return liveStreamEvents; } @@ -115,9 +139,12 @@ public class LIveStreamServiceImpl implements LiveStreamService { private LiveStreamEventImpl toLiveStreamEvent(KalendarEvent event, boolean timeOnly) { LiveStreamEventImpl liveStreamEvent = new LiveStreamEventImpl(); liveStreamEvent.setId(event.getID()); - liveStreamEvent.setBegin(event.getBegin()); - liveStreamEvent.setEnd(event.getEnd()); liveStreamEvent.setAllDayEvent(event.isAllDayEvent()); + liveStreamEvent.setBegin(event.getBegin()); + Date end = event.isAllDayEvent() + ? getEndOfDay(event.getEnd()) + : event.getEnd(); + liveStreamEvent.setEnd(end); liveStreamEvent.setLiveStreamUrl(event.getLiveStreamUrl()); if (!timeOnly) { liveStreamEvent.setSubject(event.getSubject()); @@ -127,4 +154,18 @@ public class LIveStreamServiceImpl implements LiveStreamService { return liveStreamEvent; } + private Date getEndOfDay(Date date) { + LocalDateTime localDateTime = dateToLocalDateTime(date); + LocalDateTime endOfDay = localDateTime.with(LocalTime.MAX); + return localDateTimeToDate(endOfDay); + } + + private LocalDateTime dateToLocalDateTime(Date date) { + return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()); + } + + private Date localDateTimeToDate(LocalDateTime localDateTime) { + return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant()); + } + } diff --git a/src/main/java/org/olat/ims/qti21/ui/components/AssessmentRenderFunctions.java b/src/main/java/org/olat/ims/qti21/ui/components/AssessmentRenderFunctions.java index d54ef1081e39e8df7f3ee834401fd4a95686805e..3e6f70d96c254b12267cb45628c9be2be9899bcf 100644 --- a/src/main/java/org/olat/ims/qti21/ui/components/AssessmentRenderFunctions.java +++ b/src/main/java/org/olat/ims/qti21/ui/components/AssessmentRenderFunctions.java @@ -31,8 +31,8 @@ import java.util.List; import java.util.Map; import org.apache.commons.codec.binary.Base64; +import org.apache.commons.io.IOUtils; import org.apache.logging.log4j.Logger; -import org.jcodec.common.IOUtils; import org.olat.core.CoreSpringFactory; import org.olat.core.gui.render.StringOutput; import org.olat.core.logging.Tracing; 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 bcfadbb58079795d2eeead38caa9e340c41e3d57..2f9c11c3cfff738d4ac3dc1085abee866f4eabca 100644 --- a/src/main/java/org/olat/modules/video/manager/VideoManagerImpl.java +++ b/src/main/java/org/olat/modules/video/manager/VideoManagerImpl.java @@ -28,9 +28,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; -import java.io.RandomAccessFile; import java.math.RoundingMode; -import java.nio.channels.FileChannel; import java.text.DecimalFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -49,14 +47,16 @@ import javax.imageio.ImageIO; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.logging.log4j.Logger; import org.jcodec.api.FrameGrab; -import org.jcodec.common.FileChannelWrapper; +import org.jcodec.common.io.FileChannelWrapper; +import org.jcodec.common.io.NIOUtils; +import org.jcodec.scale.AWTUtil; import org.olat.core.commons.services.image.Crop; import org.olat.core.commons.services.image.ImageService; import org.olat.core.commons.services.image.Size; import org.olat.core.commons.services.video.MovieService; import org.olat.core.gui.translator.Translator; -import org.apache.logging.log4j.Logger; import org.olat.core.logging.Tracing; import org.olat.core.util.FileUtils; import org.olat.core.util.Formatter; @@ -276,19 +276,16 @@ public class VideoManagerImpl implements VideoManager { public boolean getFrame(VFSLeaf video, int frameNumber, VFSLeaf frame) { File videoFile = ((LocalFileImpl)video).getBasefile(); - try (RandomAccessFile randomAccessFile = new RandomAccessFile(videoFile, "r")) { - FileChannel ch = randomAccessFile.getChannel(); - FileChannelWrapper in = new FileChannelWrapper(ch); - FrameGrab frameGrab = new FrameGrab(in).seekToFrameSloppy(frameNumber); + try (FileChannelWrapper in = NIOUtils.readableChannel(videoFile)) { + FrameGrab frameGrab = FrameGrab.createFrameGrab(in).seekToFrameSloppy(frameNumber); OutputStream frameOutputStream = frame.getOutputStream(false); - BufferedImage bufImg = frameGrab.getFrame(); + BufferedImage bufImg = AWTUtil.toBufferedImage(frameGrab.getNativeFrame()); ImageIO.write(bufImg, "JPG", frameOutputStream); // close everything to prevent resource leaks frameOutputStream.close(); in.close(); - ch.close(); return true; } catch (Exception | AssertionError e) { @@ -303,14 +300,11 @@ public class VideoManagerImpl implements VideoManager { BufferedImage bufImg = null; boolean imgBlack = true; int countBlack = 0; - try (RandomAccessFile randomAccessFile = new RandomAccessFile(videoFile, "r")) { + try (FileChannelWrapper in = NIOUtils.readableChannel(videoFile)) { OutputStream frameOutputStream = frame.getOutputStream(false); - - FileChannel ch = randomAccessFile.getChannel(); - FileChannelWrapper in = new FileChannelWrapper(ch); - FrameGrab frameGrab = new FrameGrab(in).seekToFrameSloppy(frameNumber); + FrameGrab frameGrab = FrameGrab.createFrameGrab(in).seekToFrameSloppy(frameNumber); - bufImg = frameGrab.getFrame(); + bufImg = AWTUtil.toBufferedImage(frameGrab.getNativeFrame()); int xmin = bufImg.getMinX(); int ymin = bufImg.getMinY(); @@ -343,7 +337,6 @@ public class VideoManagerImpl implements VideoManager { // close everything to prevent resource leaks frameOutputStream.close(); in.close(); - ch.close(); return imgBlack; } catch (Exception | AssertionError e) {