io.github.ericmedvet.mrsim2d.viewer.OnlineVideoBuilder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mrsim2d.viewer Show documentation
Show all versions of mrsim2d.viewer Show documentation
Visual components (video maker and GUI) for 2D-MR-Sim.
The newest version!
/*-
* ========================LICENSE_START=================================
* mrsim2d-viewer
* %%
* Copyright (C) 2020 - 2024 Eric Medvet
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* =========================LICENSE_END==================================
*/
package io.github.ericmedvet.mrsim2d.viewer;
import io.github.ericmedvet.mrsim2d.core.Snapshot;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
public class OnlineVideoBuilder implements Accumulator {
private static final Logger L = Logger.getLogger(OnlineVideoBuilder.class.getName());
private final int w;
private final int h;
private final double startTime;
private final double endTime;
private final double frameRate;
private final VideoUtils.EncoderFacility encoder;
private final File file;
private final Drawer drawer;
private final List images;
private final List snapshots;
private final List snapshotTs;
private double lastDrawnT;
private double lastT;
public OnlineVideoBuilder(
int w,
int h,
double startTime,
double endTime,
double frameRate,
VideoUtils.EncoderFacility encoder,
File file,
Drawer drawer) {
this.w = w;
this.h = h;
this.startTime = startTime;
this.endTime = endTime;
this.frameRate = frameRate;
this.encoder = encoder;
this.file = file;
this.drawer = drawer;
images = new ArrayList<>((int) Math.ceil((endTime - startTime) * frameRate));
snapshots = new ArrayList<>();
snapshotTs = new ArrayList<>();
lastT = Double.NaN;
}
@Override
public void accept(Snapshot snapshot) {
if (!Double.isNaN(lastT)) {
snapshotTs.add(snapshot.t() - lastT);
}
lastT = snapshot.t();
if (snapshot.t() < startTime || snapshot.t() > endTime) {
return;
}
double tolerance = snapshotTs.stream().mapToDouble(t -> t).average().orElse(0d) / 2d;
snapshots.add(snapshot);
if (snapshot.t() >= lastDrawnT + (1d / frameRate) - tolerance) {
lastDrawnT = snapshot.t();
// create image
BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_3BYTE_BGR);
// draw
Graphics2D g = image.createGraphics();
g.setClip(0, 0, image.getWidth(), image.getHeight());
drawer.draw(snapshots, g);
g.dispose();
// add
images.add(image);
snapshots.clear();
}
}
@Override
public File get() {
if (images.isEmpty()) {
L.warning("No snapshot to save: abort");
return null;
}
L.fine(String.format("Saving video on %s", file));
try {
Instant encodingStartInstant = Instant.now();
VideoUtils.encodeAndSave(images, frameRate, file, encoder);
L.fine(String.format(
"Video saved: %.1fMB written in %.2fs",
Files.size(file.toPath()) / 1024f / 1024f,
Duration.between(encodingStartInstant, Instant.now()).toMillis() / 1000f));
} catch (IOException e) {
L.severe(String.format("Cannot save file due to %s", e));
return null;
}
return file;
}
}