This is an automated email from the ASF dual-hosted git repository.
solomax pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/openmeetings.git
The following commit(s) were added to refs/heads/master by this push:
new f1b65ef [OPENMEETINGS-2592] recording in interview room should work,
code clean-up
new 84b0783 Merge branch 'master' of github.com:apache/openmeetings
f1b65ef is described below
commit f1b65efd2daaf8654ca531ae8eece5a47a5abb79
Author: Maxim Solodovnik <[email protected]>
AuthorDate: Fri Mar 12 07:58:54 2021 +0700
[OPENMEETINGS-2592] recording in interview room should work, code clean-up
---
.../openmeetings/core/converter/BaseConverter.java | 54 +++----
.../core/converter/ImageConverter.java | 31 ++--
.../core/converter/InterviewConverter.java | 161 ++++++++++++---------
.../core/converter/RecordingConverter.java | 2 +-
.../core/converter/VideoConverter.java | 12 +-
.../apache/openmeetings/core/remote/KStream.java | 128 +++++++++-------
openmeetings-db/pom.xml | 1 -
.../openmeetings/util/process/ProcessHelper.java | 21 ++-
.../web/pages/install/InstallWizard.java | 11 +-
9 files changed, 231 insertions(+), 190 deletions(-)
diff --git
a/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/BaseConverter.java
b/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/BaseConverter.java
index 941c31d..e19df90 100644
---
a/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/BaseConverter.java
+++
b/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/BaseConverter.java
@@ -62,7 +62,7 @@ public abstract class BaseConverter {
private static final Pattern p = Pattern.compile("\\d{2,5}(x)\\d{2,5}");
public static final String EXEC_EXT =
System.getProperty("os.name").toUpperCase(Locale.ROOT).indexOf("WINDOWS") < 0 ?
"" : ".exe";
private static final int MINUTE_MULTIPLIER = 60 * 1000;
- public static final int TIME_TO_WAIT_FOR_FRAME = 15 * MINUTE_MULTIPLIER;
+ public static final int TIME_TO_WAIT_FOR_FRAME = 5 * MINUTE_MULTIPLIER;
public static final double HALF_STEP = 1. / 2;
@Autowired
@@ -140,7 +140,7 @@ public abstract class BaseConverter {
}
}
- private String[] mergeAudioToWaves(List<File> waveFiles, File wav)
throws IOException {
+ private List<String> mergeAudioToWaves(List<File> waveFiles, File wav)
throws IOException {
List<String> argv = new ArrayList<>();
argv.add(getPathToSoX());
@@ -150,7 +150,7 @@ public abstract class BaseConverter {
}
argv.add(wav.getCanonicalPath());
- return argv.toArray(new String[0]);
+ return argv;
}
protected void createWav(Recording r, ProcessResultList logs, File
streamFolder, List<File> waveFiles, File wav, List<RecordingChunk> chunks)
throws IOException {
@@ -163,27 +163,25 @@ public abstract class BaseConverter {
// Calculate delta at beginning
double duration = diffSeconds(r.getRecordEnd(),
r.getRecordStart());
- String[] cmd = new String[] { getPathToSoX(),
oneSecWav, wav.getCanonicalPath(), "pad", "0", String.valueOf(duration) };
+ List<String> cmd = List.of(getPathToSoX(), oneSecWav,
wav.getCanonicalPath(), "pad", "0", String.valueOf(duration));
-
logs.add(ProcessHelper.executeScript("generateSampleAudio", cmd));
+ logs.add(ProcessHelper.exec("generateSampleAudio",
cmd));
} else if (waveFiles.size() == 1) {
copyFile(waveFiles.get(0), wav);
} else {
- String[] soxArgs = mergeAudioToWaves(waveFiles, wav);
-
-
logs.add(ProcessHelper.executeScript("mergeAudioToWaves", soxArgs));
+ logs.add(ProcessHelper.exec("mergeAudioToWaves",
mergeAudioToWaves(waveFiles, wav)));
}
}
- private String[] addSoxPad(ProcessResultList logs, String job, double
length, double position, File inFile, File outFile) throws IOException {
+ private List<String> addSoxPad(ProcessResultList logs, String job,
double length, double position, File inFile, File outFile) throws IOException {
if (length < 0 || position < 0) {
log.debug("::addSoxPad {} Invalid parameters: length =
{}; position = {}; inFile = {}", job, length, position, inFile);
}
- String[] argv = new String[] { getPathToSoX(),
inFile.getCanonicalPath(), outFile.getCanonicalPath(), "pad"
+ List<String> argv = List.of(getPathToSoX(),
inFile.getCanonicalPath(), outFile.getCanonicalPath(), "pad"
, String.valueOf(length < 0 ? 0 : length)
- , String.valueOf(position < 0 ? 0 : position) };
+ , String.valueOf(position < 0 ? 0 : position));
- logs.add(ProcessHelper.executeScript(job, argv));
+ logs.add(ProcessHelper.exec(job, argv));
return argv;
}
@@ -192,7 +190,7 @@ public abstract class BaseConverter {
log.debug("### {}:: recording id {}; stream with id {};
current status: {} ", prefix, chunk.getRecording().getId()
, chunk.getId(),
chunk.getStreamStatus());
File chunkFlv =
getRecordingChunk(chunk.getRecording().getRoomId(), chunk.getStreamName());
- log.debug("### {}:: Flv file [{}] exists ? {}; size:
{}, lastModified: {} ", prefix, chunkFlv.getPath(), chunkFlv.exists(),
chunkFlv.length(), chunkFlv.lastModified());
+ log.debug("### {}:: Chunk file [{}] exists ? {}; size:
{}, lastModified: {} ", prefix, chunkFlv.getPath(), chunkFlv.exists(),
chunkFlv.length(), chunkFlv.lastModified());
}
}
@@ -259,13 +257,13 @@ public abstract class BaseConverter {
log.debug("FLV File Name: {} Length: {} ",
inputFlvFile.getName(), inputFlvFile.length());
if (inputFlvFile.exists()) {
- String[] argv = new String[] {
+ List<String> argv = List.of(
getPathToFFMPEG(), "-y"
, "-i",
inputFlvFile.getCanonicalPath()
, "-af",
String.format("aresample=%s:min_comp=0.001:min_hard_comp=0.100000",
getAudioBitrate())
- ,
outputWav.getCanonicalPath()};
+ ,
outputWav.getCanonicalPath());
//there might be no audio in the stream
-
logs.add(ProcessHelper.executeScript("stripAudioFromFLVs", argv, true));
+
logs.add(ProcessHelper.exec("stripAudioFromFLVs", argv, true));
}
if (outputWav.exists() && outputWav.length() !=
0) {
@@ -319,13 +317,12 @@ public abstract class BaseConverter {
return List.of();
}
- private List<String> addMp4OutParams(Recording r, List<String> argv,
String mp4path) {
+ private List<String> addMp4OutParams(Recording r, List<String> argv,
boolean interview, String mp4path) {
argv.addAll(List.of(
"-c:v", "h264" //
, "-crf", "24"
, "-vsync", "0"
, "-pix_fmt", "yuv420p"
- , "-vf", "pad=ceil(iw/2)*2:ceil(ih/2)*2"
, "-preset", getVideoPreset()
, "-profile:v", "baseline"
, "-level", "3.0"
@@ -334,16 +331,19 @@ public abstract class BaseConverter {
, "-ar", String.valueOf(getAudioRate())
, "-b:a", getAudioBitrate()
));
+ if (!interview) {
+ argv.addAll(List.of("-vf",
"pad=ceil(iw/2)*2:ceil(ih/2)*2"));
+ }
argv.addAll(additionalMp4OutParams(r));
argv.add(mp4path);
return argv;
}
- protected String convertToMp4(Recording r, List<String> inArgv,
ProcessResultList logs) throws IOException {
+ protected String convertToMp4(Recording r, List<String> inArgv, boolean
interview, ProcessResultList logs) throws IOException {
String mp4path = r.getFile().getCanonicalPath();
List<String> argv = new ArrayList<>(List.of(getPathToFFMPEG(),
"-y"));
argv.addAll(inArgv);
- logs.add(ProcessHelper.executeScript("generate MP4",
addMp4OutParams(r, argv, mp4path).toArray(new String[]{})));
+ logs.add(ProcessHelper.exec("generate MP4", addMp4OutParams(r,
argv, interview, mp4path)));
return mp4path;
}
@@ -351,13 +351,13 @@ public abstract class BaseConverter {
// Extract first Image for preview purpose
// ffmpeg -i movie.mp4 -vf "thumbnail,scale=640:-1" -frames:v
1 movie.png
File png = f.getFile(EXTENSION_PNG);
- String[] argv = new String[] { //
- getPathToFFMPEG(), "-y" //
- , "-i", mp4path //
- , "-vf", "thumbnail,scale=640:-1" //
- , "-frames:v", "1" //
- , png.getCanonicalPath() };
- logs.add(ProcessHelper.executeScript(String.format("generate
preview PNG :: %s", f.getHash()), argv));
+ List<String> argv = List.of(
+ getPathToFFMPEG(), "-y"
+ , "-i", mp4path
+ , "-vf", "thumbnail,scale=640:-1"
+ , "-frames:v", "1"
+ , png.getCanonicalPath());
+ logs.add(ProcessHelper.exec("generate preview PNG :: " +
f.getHash(), argv));
}
/**
diff --git
a/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/ImageConverter.java
b/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/ImageConverter.java
index c327dda..a6dc993 100644
---
a/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/ImageConverter.java
+++
b/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/ImageConverter.java
@@ -33,6 +33,7 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.util.List;
import java.util.Optional;
import java.util.function.DoubleConsumer;
@@ -152,17 +153,16 @@ public class ImageConverter extends BaseConverter {
*
*/
private ProcessResult convertSinglePng(File in, File out) throws
IOException {
- String[] argv = new String[] { getPathToConvert(),
in.getCanonicalPath(), out.getCanonicalPath() };
+ List<String> argv = List.of(getPathToConvert(),
in.getCanonicalPath(), out.getCanonicalPath());
- return ProcessHelper.executeScript("convertSinglePng", argv);
+ return ProcessHelper.exec("convertSinglePng", argv);
}
public ProcessResult resize(File in, File out, Integer width, Integer
height) throws IOException {
- String[] argv = new String[] { getPathToConvert()
+ List<String> argv = List.of(getPathToConvert()
, "-resize", (width == null ? "" : width) +
(height == null ? "" : "x" + height)
- , in.getCanonicalPath(), out.getCanonicalPath()
- };
- return ProcessHelper.executeScript("resize", argv);
+ , in.getCanonicalPath(),
out.getCanonicalPath());
+ return ProcessHelper.exec("resize", argv);
}
/**
@@ -176,16 +176,15 @@ public class ImageConverter extends BaseConverter {
*/
public ProcessResultList convertDocument(FileItem f, File pdf,
ProcessResultList logs, Optional<DoubleConsumer> progress) throws IOException {
log.debug("convertDocument");
- String[] argv = new String[] {
- getPathToConvert()
- , "-density", getDpi()
- , "-define", "pdf:use-cropbox=true"
- , pdf.getCanonicalPath()
- , "+profile", "'*'"
- , "-quality", getQuality()
- , new File(pdf.getParentFile(),
PAGE_TMPLT).getCanonicalPath()
- };
- ProcessResult res = ProcessHelper.executeScript("convert PDF to
images", argv);
+ List<String> argv = List.of(
+ getPathToConvert()
+ , "-density", getDpi()
+ , "-define", "pdf:use-cropbox=true"
+ , pdf.getCanonicalPath()
+ , "+profile", "'*'"
+ , "-quality", getQuality()
+ , new File(pdf.getParentFile(),
PAGE_TMPLT).getCanonicalPath());
+ ProcessResult res = ProcessHelper.exec("convert PDF to images",
argv);
logs.add(res);
progress.ifPresent(theProgress -> theProgress.accept(1. / 4));
if (res.isOk()) {
diff --git
a/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/InterviewConverter.java
b/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/InterviewConverter.java
index 7e17d93..d74c287 100644
---
a/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/InterviewConverter.java
+++
b/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/InterviewConverter.java
@@ -48,6 +48,24 @@ import org.springframework.stereotype.Component;
@Component
public class InterviewConverter extends BaseConverter implements
IRecordingConverter {
private static final Logger log =
LoggerFactory.getLogger(InterviewConverter.class);
+ private static final int WIDTH = 320;
+ private static final int HEIGHT = 260;
+ private String interviewCam;
+ private String interviewBlank;
+
+ private void init() throws ConversionException, IOException {
+ // Default Image for empty interview video pods
+ final File interviewCamFile = new
File(OmFileHelper.getImagesDir(), "interview_webcam.png");
+ if (!interviewCamFile.exists()) {
+ throw new
ConversionException("defaultInterviewImageFile does not exist!");
+ }
+ interviewCam = interviewCamFile.getCanonicalPath();
+ final File interviewBlankFile = new
File(OmFileHelper.getImagesDir(), "blank.png");
+ if (!interviewBlankFile.exists()) {
+ throw new
ConversionException("defaultInterviewImageFile does not exist!");
+ }
+ interviewBlank = interviewBlankFile.getCanonicalPath();
+ }
@Override
public void startConversion(Recording r) {
@@ -58,13 +76,10 @@ public class InterviewConverter extends BaseConverter
implements IRecordingConve
ProcessResultList logs = new ProcessResultList();
List<File> waveFiles = new ArrayList<>();
try {
- // Default Image for empty interview video pods
- final File interviewCamFile = new
File(OmFileHelper.getImagesDir(), "interview_webcam.png");
- if (!interviewCamFile.exists()) {
- throw new
ConversionException("defaultInterviewImageFile does not exist!");
- }
-
log.debug("recording {}", r.getId());
+ if (interviewCam == null) {
+ init();
+ }
if (Strings.isEmpty(r.getHash())) {
r.setHash(randomUUID().toString());
}
@@ -77,10 +92,6 @@ public class InterviewConverter extends BaseConverter
implements IRecordingConve
File wav = new File(streamFolder,
String.format("INTERVIEW_%s_FINAL_WAVE.wav", r.getId()));
createWav(r, logs, streamFolder, waveFiles, wav,
chunks);
- final String interviewCam =
interviewCamFile.getCanonicalPath();
-
- final int width = 320;
- final int height = 260;
// Merge Audio with Video / Calculate resulting video
// group by sid first to get all pods
@@ -89,61 +100,21 @@ public class InterviewConverter extends BaseConverter
implements IRecordingConve
, LinkedHashMap::new
,
Collectors.collectingAndThen(Collectors.toList(), l ->
l.stream().sorted(Comparator.comparing(RecordingChunk::getStart)).collect(Collectors.toList()))));
List<String> pods = new ArrayList<>();
- int numPods = pods.size();
for (Entry<String, List<RecordingChunk>> e :
cunksBySid.entrySet()) {
+ int podIdx = pods.size();
Date pStart = r.getRecordStart();
List<PodPart> parts = new ArrayList<>();
- pStart = processParts(r.getRoomId(),
e.getValue(), logs, numPods, parts, pStart);
+ pStart = processParts(r.getRoomId(),
e.getValue(), logs, podIdx, parts, pStart);
if (!parts.isEmpty()) {
- String podX = new File(streamFolder,
String.format("rec_%s_pod_%s.%s", r.getId(), numPods,
EXTENSION_MP4)).getCanonicalPath();
+ String podX = new File(streamFolder,
String.format("rec_%s_pod_%s.%s", r.getId(), podIdx,
EXTENSION_MP4)).getCanonicalPath();
long diff = diff(r.getRecordEnd(),
pStart);
PodPart.add(parts, diff);
- /* create continuous pod
- * ffmpeg \
- * -loop 1 -framerate 24 -t 10 -i
image1.jpg \
- * -i video.mp4 \
- * -loop 1 -framerate 24 -t 10 -i
image2.jpg \
- * -loop 1 -framerate 24 -t 10 -i
image3.jpg \
- * -filter_complex
"[0][1][2][3]concat=n=4:v=1:a=0" out.mp4
- */
- List<String> args = new ArrayList<>();
- args.add(getPathToFFMPEG());
- args.add("-y");
- StringBuilder videos = new
StringBuilder();
- StringBuilder concat = new
StringBuilder();
- for (int i = 0; i < parts.size(); ++i) {
- PodPart p = parts.get(i);
- if (p.getFile() == null) {
- args.add("-loop");
- args.add("1");
- args.add("-t");
-
args.add(formatMillis(p.getDuration()));
- args.add("-i");
- args.add(interviewCam);
- } else {
- args.add("-t");
-
args.add(formatMillis(p.getDuration()));
- args.add("-i");
- args.add(p.getFile());
- }
-
videos.append('[').append(i).append(']')
-
.append("scale=").append(width).append(':').append(height).append(",setsar=1:1")
-
.append("[v").append(i).append("]; ");
-
concat.append("[v").append(i).append(']');
- }
- args.add("-filter_complex");
- args.add(concat.insert(0,
videos).append("concat=n=").append(parts.size()).append(":v=1:a=0").toString());
- args.add("-an");
- args.add(podX);
- ProcessResult res =
ProcessHelper.executeScript(String.format("Full video pod_%s", numPods),
args.toArray(new String[0]), true);
- logs.add(res);
- if (res.isWarn()) {
- throw new
ConversionException("Fail to create pod");
- }
+
+ createPod(podX, interviewCam, podIdx,
parts, logs);
pods.add(podX);
- numPods = pods.size();
}
}
+ int numPods = pods.size();
if (numPods == 0) {
ProcessResult res = new ProcessResult();
res.setProcess("CheckStreamFilesExists");
@@ -155,11 +126,18 @@ public class InterviewConverter extends BaseConverter
implements IRecordingConve
double ratio = Math.sqrt(numPods / Math.sqrt(2));
int w = ratio < 1 ? numPods : (int)Math.round(ratio);
w = Math.max(w, (int)Math.round(1. * numPods / w));
+ List<PodPart> missingParts = new ArrayList<>();
+ PodPart.add(missingParts, diff(r.getRecordEnd(),
r.getRecordStart()));
+ String missingPod = new File(streamFolder,
String.format("rec_%s_pod_%s.%s", r.getId(), numPods,
EXTENSION_MP4)).getCanonicalPath();
+ createPod(missingPod, interviewBlank, numPods,
missingParts, logs);
+ for (int i = numPods % w; i < w; ++i) {
+ pods.add(missingPod);
+ }
- r.setWidth(w * width);
- r.setHeight((numPods / w) * height);
+ r.setWidth(w * WIDTH);
+ r.setHeight((numPods / w) * HEIGHT);
- String mp4path = convertToMp4(r, getFinalArgs(numPods,
pods, wav, w), logs);
+ String mp4path = convertToMp4(r, getFinalArgs(pods,
wav, w), numPods != 1, logs);
finalizeRec(r, mp4path, logs);
} catch (Exception err) {
@@ -175,6 +153,51 @@ public class InterviewConverter extends BaseConverter
implements IRecordingConve
}
}
+ private void createPod(String podX, String image, int podIdx,
List<PodPart> parts, ProcessResultList logs) throws ConversionException {
+ /* create continuous pod
+ * ffmpeg \
+ * -loop 1 -framerate 24 -t 10 -i image1.jpg \
+ * -i video.mp4 \
+ * -loop 1 -framerate 24 -t 10 -i image2.jpg \
+ * -loop 1 -framerate 24 -t 10 -i image3.jpg \
+ * -filter_complex "[0][1][2][3]concat=n=4:v=1:a=0" out.mp4
+ */
+ List<String> args = new ArrayList<>();
+ args.add(getPathToFFMPEG());
+ args.add("-y");
+ StringBuilder videos = new StringBuilder();
+ StringBuilder concat = new StringBuilder();
+ for (int i = 0; i < parts.size(); ++i) {
+ PodPart p = parts.get(i);
+ if (p.getFile() == null) {
+ args.add("-loop");
+ args.add("1");
+ args.add("-t");
+ args.add(formatMillis(p.getDuration()));
+ args.add("-i");
+ args.add(image);
+ } else {
+ args.add("-t");
+ args.add(formatMillis(p.getDuration()));
+ args.add("-i");
+ args.add(p.getFile());
+ }
+ videos.append('[').append(i).append(']')
+
.append("scale=").append(WIDTH).append(':').append(HEIGHT).append(",setsar=1:1")
+ .append("[v").append(i).append("]; ");
+ concat.append("[v").append(i).append(']');
+ }
+ args.add("-filter_complex");
+ args.add(concat.insert(0,
videos).append("concat=n=").append(parts.size()).append(":v=1:a=0").toString());
+ args.add("-an");
+ args.add(podX);
+ ProcessResult res = ProcessHelper.exec("Full video pod_" +
podIdx, args, true);
+ logs.add(res);
+ if (res.isWarn()) {
+ throw new ConversionException("Fail to create pod");
+ }
+ }
+
private Date processParts(Long roomId, List<RecordingChunk> chunks,
ProcessResultList logs, int numPods, List<PodPart> parts, Date pStart) throws
IOException {
for (RecordingChunk chunk : chunks) {
File chunkStream = getRecordingChunk(roomId,
chunk.getStreamName());
@@ -186,12 +209,12 @@ public class InterviewConverter extends BaseConverter
implements IRecordingConve
/* CHECK FILE:
* ffmpeg -i
rec_316_stream_567_2013_08_28_11_51_45.webm -v error -f null file.null
*/
- String[] args = new String[] {getPathToFFMPEG(), "-y"
+ List<String> args = List.of(getPathToFFMPEG(), "-y"
, "-i", path
, "-v", "error"
, "-f", "null"
- , "file.null"};
- ProcessResult res =
ProcessHelper.executeScript(String.format("Check chunk pod video_%s_%s",
numPods, parts.size()), args, true);
+ , "file.null");
+ ProcessResult res =
ProcessHelper.exec(String.format("Check chunk pod video_%s_%s", numPods,
parts.size()), args, true);
logs.add(res);
if (!res.isWarn()) {
long diff = diff(chunk.isAudioOnly() ?
chunk.getEnd() : chunk.getStart(), pStart);
@@ -205,7 +228,8 @@ public class InterviewConverter extends BaseConverter
implements IRecordingConve
return pStart;
}
- private static List<String> getFinalArgs(int numPods, List<String>
pods, File wav, int w) throws IOException {
+ private static List<String> getFinalArgs(List<String> pods, File wav,
int w) throws IOException {
+ final int numPods = pods.size();
List<String> args = new ArrayList<>();
if (numPods == 1) {
args.add("-i");
@@ -222,12 +246,14 @@ public class InterviewConverter extends BaseConverter
implements IRecordingConve
*/
StringBuilder cols = new StringBuilder();
StringBuilder rows = new StringBuilder();
+ int colCount = 0;
for (int i = 0, j = 0; i < numPods; ++i) {
+ colCount++;
args.add("-i");
args.add(pods.get(i));
cols.append('[').append(i).append(":v]");
- if (i != 0 && (i + 1) % w == 0) {
- cols.append("hstack=inputs=").append(w);
+ if (i != 0 && colCount % w == 0) {
+
cols.append("hstack=inputs=").append(colCount);
if (j == 0 && i == numPods - 1) {
cols.append("[v]");
} else {
@@ -235,10 +261,11 @@ public class InterviewConverter extends BaseConverter
implements IRecordingConve
}
rows.append("[c").append(j).append(']');
j++;
+ colCount = 0;
}
if (i == numPods - 1) {
if (j > 1) {
-
rows.append("vstack=inputs=").append(j).append("[v]");
+
rows.append("vstack=inputs=").append(j).append("[out];[out]pad=ceil(iw/2)*2:ceil(ih/2)*2[v]");
} else {
rows.setLength(0);
}
@@ -252,7 +279,7 @@ public class InterviewConverter extends BaseConverter
implements IRecordingConve
args.add("[v]");
}
args.add("-map");
- args.add(String.format("%s:a", numPods));
+ args.add(numPods + ":a");
args.add("-qmax"); args.add("1");
args.add("-qmin"); args.add("1");
return args;
diff --git
a/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/RecordingConverter.java
b/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/RecordingConverter.java
index f1afb49..80ccbae 100644
---
a/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/RecordingConverter.java
+++
b/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/RecordingConverter.java
@@ -88,7 +88,7 @@ public class RecordingConverter extends BaseConverter
implements IRecordingConve
String mp4path = convertToMp4(r, List.of(
"-itsoffset",
formatMillis(diff(screenChunk.getStart(), r.getRecordStart())),
"-i", inputScreenFullFlv, "-i",
wav.getCanonicalPath()
- ), logs);
+ ), false, logs);
Dimension dim = getDimension(logs.getLast().getError(),
null); // will return 100x100 for non-video to be able to play
if (dim != null) {
r.setWidth(dim.getWidth());
diff --git
a/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/VideoConverter.java
b/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/VideoConverter.java
index 0f444ae..96be9f5 100644
---
a/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/VideoConverter.java
+++
b/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/VideoConverter.java
@@ -62,13 +62,13 @@ public class VideoConverter extends BaseConverter {
List<String> args = new
ArrayList<>(List.of(getPathToFFMPEG(), "-y"));
if (sf.isAudio()) {
// need to add background image, it should be
jpg since black on transparent will be invisible
- args.addAll(List.of("-loop", "1"//
- , "-framerate", "24"//
+ args.addAll(List.of("-loop", "1"
+ , "-framerate", "24"
, "-i", new
File(getCssImagesDir(), "audio.jpg").getCanonicalPath()));
}
- args.addAll(List.of("-i", input //
- , "-c:v", "h264" //
- , "-c:a", "aac" //
+ args.addAll(List.of("-i", input
+ , "-c:v", "h264"
+ , "-c:a", "aac"
, "-pix_fmt", "yuv420p"
, "-vf", "pad=ceil(iw/2)*2:ceil(ih/2)*2"
));
@@ -76,7 +76,7 @@ public class VideoConverter extends BaseConverter {
args.add("-shortest");
}
args.add(mp4.getCanonicalPath());
- ProcessResult res =
ProcessHelper.executeScript("convert to MP4 :: " + f.getHash(),
args.toArray(new String[0]));
+ ProcessResult res = ProcessHelper.exec("convert to MP4
:: " + f.getHash(), args);
logs.add(res);
progress.ifPresent(theProgress ->
theProgress.accept(STEP));
if (sameExt && tmp != null) {
diff --git
a/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/KStream.java
b/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/KStream.java
index aaef0ca..56f2a58 100644
---
a/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/KStream.java
+++
b/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/KStream.java
@@ -339,7 +339,10 @@ public class KStream extends AbstractStream implements
ISipCallbacks {
setTags(recorder, uid);
recorder.addRecordingListener(evt -> chunkId =
kRoom.getChunkDao().start(kRoom.getRecordingId(), type, chunkUid, sid));
- recorder.addStoppedListener(evt ->
kRoom.getChunkDao().stop(chunkId));
+ recorder.addStoppedListener(evt -> {
+ kRoom.getChunkDao().stop(chunkId);
+ chunkId = null;
+ });
switch (profile) {
case WEBM:
outgoingMedia.connect(recorder,
MediaType.AUDIO);
@@ -367,8 +370,7 @@ public class KStream extends AbstractStream implements
ISipCallbacks {
}
public void stopRecord() {
- releaseRecorder(true);
- chunkId = null;
+ stopRecorder(true, () -> {});
}
public void remove(final Client c) {
@@ -423,77 +425,93 @@ public class KStream extends AbstractStream implements
ISipCallbacks {
public void release(boolean remove) {
if (outgoingMedia != null) {
releaseListeners();
- releaseRecorder(false);
- releaseRtp();
- outgoingMedia.release(new Continuation<Void>() {
- @Override
- public void onSuccess(Void result) throws
Exception {
- log.trace("PARTICIPANT {}: Released
successfully", KStream.this.uid);
- }
+ stopRecorder(false, () -> {
+ releaseRtp();
+ outgoingMedia.release(new Continuation<Void>() {
+ @Override
+ public void onSuccess(Void result)
throws Exception {
+ log.trace("PARTICIPANT {}:
Released successfully", KStream.this.uid);
+ }
- @Override
- public void onError(Throwable cause) throws
Exception {
- log.warn("PARTICIPANT {}: Could not
release", KStream.this.uid, cause);
- }
- });
- pipeline.release(new Continuation<Void>() {
- @Override
- public void onSuccess(Void result) throws
Exception {
- log.trace("PARTICIPANT {}: Released
Pipeline", KStream.this.uid);
- }
+ @Override
+ public void onError(Throwable cause)
throws Exception {
+ log.warn("PARTICIPANT {}: Could
not release", KStream.this.uid, cause);
+ }
+ });
+ pipeline.release(new Continuation<Void>() {
+ @Override
+ public void onSuccess(Void result)
throws Exception {
+ log.trace("PARTICIPANT {}:
Released Pipeline", KStream.this.uid);
+ }
- @Override
- public void onError(Throwable cause) throws
Exception {
- log.warn("PARTICIPANT {}: Could not
release Pipeline", KStream.this.uid, cause);
- }
+ @Override
+ public void onError(Throwable cause)
throws Exception {
+ log.warn("PARTICIPANT {}: Could
not release Pipeline", KStream.this.uid, cause);
+ }
+ });
+ outgoingMedia = null;
+ doRemove(remove);
});
- outgoingMedia = null;
+ } else {
+ doRemove(remove);
}
+ }
+
+ private void doRemove(boolean remove) {
if (remove) {
kHandler.getStreamProcessor().release(this, false);
}
}
- private void releaseRecorder(boolean wait) {
- if (recorder != null) {
- if (wait) {
- recorder.stopAndWait();
- } else {
- recorder.stop(new Continuation<Void>() {
- @Override
- public void onSuccess(Void result)
throws Exception {
- log.trace("PARTICIPANT {}:
Recording stopped", KStream.this.uid);
- }
+ private void releaseRecorder(Runnable then) {
+ outgoingMedia.disconnect(recorder, new Continuation<Void>() {
+ @Override
+ public void onSuccess(Void result) throws Exception {
+ log.trace("PARTICIPANT {}: Recorder
disconnected successfully", KStream.this.uid);
+ }
- @Override
- public void onError(Throwable cause)
throws Exception {
- log.warn("PARTICIPANT {}: Could
not stop recording", KStream.this.uid, cause);
- }
- });
+ @Override
+ public void onError(Throwable cause) throws Exception {
+ log.warn("PARTICIPANT {}: Could not disconnect
recorder", KStream.this.uid, cause);
+ }
+ });
+ recorder.release(new Continuation<Void>() {
+ @Override
+ public void onSuccess(Void result) throws Exception {
+ log.trace("PARTICIPANT {}: Recorder released
successfully", KStream.this.uid);
}
- outgoingMedia.disconnect(recorder, new
Continuation<Void>() {
- @Override
- public void onSuccess(Void result) throws
Exception {
- log.trace("PARTICIPANT {}: Recorder
disconnected successfully", KStream.this.uid);
- }
- @Override
- public void onError(Throwable cause) throws
Exception {
- log.warn("PARTICIPANT {}: Could not
disconnect recorder", KStream.this.uid, cause);
- }
- });
- recorder.release(new Continuation<Void>() {
+ @Override
+ public void onError(Throwable cause) throws Exception {
+ log.warn("PARTICIPANT {}: Could not release
recorder", KStream.this.uid, cause);
+ }
+ });
+ recorder = null;
+ then.run();
+ }
+
+ private void stopRecorder(boolean wait, Runnable then) {
+ if (recorder != null) {
+ final Continuation<Void> stop = new Continuation<>() {
@Override
public void onSuccess(Void result) throws
Exception {
- log.trace("PARTICIPANT {}: Recorder
released successfully", KStream.this.uid);
+ log.trace("PARTICIPANT {}: Recording
stopped", KStream.this.uid);
+ releaseRecorder(then);
}
@Override
public void onError(Throwable cause) throws
Exception {
- log.warn("PARTICIPANT {}: Could not
release recorder", KStream.this.uid, cause);
+ log.warn("PARTICIPANT {}: Could not
stop recording", KStream.this.uid, cause);
+ releaseRecorder(then);
}
- });
- recorder = null;
+ };
+ if (wait) {
+ recorder.stopAndWait(stop);
+ } else {
+ recorder.stop(stop);
+ }
+ } else {
+ then.run();
}
}
diff --git a/openmeetings-db/pom.xml b/openmeetings-db/pom.xml
index d64fccb..af04d2f 100644
--- a/openmeetings-db/pom.xml
+++ b/openmeetings-db/pom.xml
@@ -60,7 +60,6 @@
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
- <version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
diff --git
a/openmeetings-util/src/main/java/org/apache/openmeetings/util/process/ProcessHelper.java
b/openmeetings-util/src/main/java/org/apache/openmeetings/util/process/ProcessHelper.java
index 262b60d..4c0f596 100644
---
a/openmeetings-util/src/main/java/org/apache/openmeetings/util/process/ProcessHelper.java
+++
b/openmeetings-util/src/main/java/org/apache/openmeetings/util/process/ProcessHelper.java
@@ -26,6 +26,7 @@ import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
+import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@@ -71,15 +72,11 @@ public class ProcessHelper {
private ProcessHelper() {}
- private static String getCommand(String[] argv) {
- StringBuilder tString = new StringBuilder();
- for (int i = 0; i < argv.length; i++) {
- tString.append(argv[i]).append(" ");
- }
- return tString.toString();
+ private static String getCommand(List<String> argv) {
+ return String.join(" ", argv);
}
- private static void debugCommandStart(String desc, String[] argv) {
+ private static void debugCommandStart(String desc, List<String> argv) {
if (log.isDebugEnabled()) {
log.debug("START {} ################# ", desc);
log.debug(getCommand(argv));
@@ -92,15 +89,15 @@ public class ProcessHelper {
}
}
- public static ProcessResult executeScript(String process, String[]
argv) {
- return executeScript(process, argv, false);
+ public static ProcessResult exec(String process, List<String> argv) {
+ return exec(process, argv, false);
}
- public static ProcessResult executeScript(String process, String[]
argv, boolean optional) {
- return executeScript(process, argv, Map.of(), optional);
+ public static ProcessResult exec(String process, List<String> argv,
boolean optional) {
+ return exec(process, argv, Map.of(), optional);
}
- private static ProcessResult executeScript(String process, String[]
argv, Map<? extends String, ? extends String> env, boolean optional) {
+ private static ProcessResult exec(String process, List<String> argv,
Map<? extends String, ? extends String> env, boolean optional) {
ProcessResult res = new ProcessResult()
.setProcess(process)
.setOptional(optional);
diff --git
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/install/InstallWizard.java
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/install/InstallWizard.java
index 0f8f322..fb52961 100644
---
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/install/InstallWizard.java
+++
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/install/InstallWizard.java
@@ -618,8 +618,9 @@ public class InstallWizard extends BootstrapWizard {
private void reportSuccess(TextField<String> path) {
path.success(path.getLabel().getObject() + " - " +
getString("54"));
}
- private boolean checkToolPath(TextField<String> path, String[]
args) {
- ProcessResult result =
ProcessHelper.executeScript(path.getInputName() + " path:: '" + path.getValue()
+ "'", args);
+
+ private boolean checkToolPath(TextField<String> path,
List<String> args) {
+ ProcessResult result =
ProcessHelper.exec(path.getInputName() + " path:: '" + path.getValue() + "'",
args);
if (!result.isOk()) {
path.error(result.getError().replaceAll(REGEX,
""));
} else {
@@ -629,15 +630,15 @@ public class InstallWizard extends BootstrapWizard {
}
private boolean checkMagicPath() {
- return checkToolPath(imageMagicPath, new String[]
{getToolPath(imageMagicPath.getValue(), "convert" + EXEC_EXT), OPT_VERSION});
+ return checkToolPath(imageMagicPath,
List.of(getToolPath(imageMagicPath.getValue(), "convert" + EXEC_EXT),
OPT_VERSION));
}
private boolean checkFfmpegPath() {
- return checkToolPath(ffmpegPath, new String[]
{getToolPath(ffmpegPath.getValue(), "ffmpeg" + EXEC_EXT), OPT_VERSION});
+ return checkToolPath(ffmpegPath,
List.of(getToolPath(ffmpegPath.getValue(), "ffmpeg" + EXEC_EXT), OPT_VERSION));
}
private boolean checkSoxPath() {
- return checkToolPath(soxPath, new String[]
{getToolPath(soxPath.getValue(), "sox" + EXEC_EXT), "--version"});
+ return checkToolPath(soxPath,
List.of(getToolPath(soxPath.getValue(), "sox" + EXEC_EXT), "--version"));
}
private boolean checkOfficePath() {