blob: f009d9ff456099627120a5b1f5f0a306ef8ca792 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.openmeetings.app.data.flvrecord.converter;
import java.io.File;
import java.util.HashMap;
import java.util.List;
import org.openmeetings.app.OpenmeetingsVariables;
import org.openmeetings.app.data.basic.Configurationmanagement;
import org.openmeetings.app.data.flvrecord.FlvRecordingMetaDataDaoImpl;
import org.openmeetings.app.data.flvrecord.FlvRecordingMetaDeltaDaoImpl;
import org.openmeetings.app.documents.GenerateSWF;
import org.openmeetings.app.persistence.beans.flvrecord.FlvRecording;
import org.openmeetings.app.persistence.beans.flvrecord.FlvRecordingMetaData;
import org.openmeetings.app.persistence.beans.flvrecord.FlvRecordingMetaDelta;
import org.openmeetings.app.remote.red5.ScopeApplicationAdapter;
import org.openmeetings.utils.ProcessHelper;
import org.red5.logging.Red5LoggerFactory;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
public abstract class BaseConverter {
private static final Logger log = Red5LoggerFactory.getLogger(
BaseConverter.class, OpenmeetingsVariables.webAppRootKey);
@Autowired
private Configurationmanagement configurationmanagement;
@Autowired
private FlvRecordingMetaDataDaoImpl flvRecordingMetaDataDaoImpl;
@Autowired
private FlvRecordingMetaDeltaDaoImpl flvRecordingMetaDeltaDaoImpl;
protected String getPathToFFMPEG() {
String pathToFFMPEG = configurationmanagement.getConfKey(3,
"ffmpeg_path").getConf_value();
if (!pathToFFMPEG.equals("") && !pathToFFMPEG.endsWith(File.separator)) {
pathToFFMPEG += File.separator;
}
pathToFFMPEG += "ffmpeg";
return pathToFFMPEG;
}
protected String getPathToSoX() {
String pathToSoX = configurationmanagement.getConfKey(3, "sox_path")
.getConf_value();
if (!pathToSoX.equals("") && !pathToSoX.endsWith(File.separator)) {
pathToSoX += File.separator;
}
pathToSoX += "sox";
return pathToSoX;
}
protected String getPathToImageMagick() {
String pathToImageMagick = this.configurationmanagement.getConfKey(3,
"imagemagick_path").getConf_value();
if (!pathToImageMagick.equals("")
&& !pathToImageMagick.endsWith(File.separator)) {
pathToImageMagick += File.separator;
}
pathToImageMagick += "convert" + GenerateSWF.execExt;
return pathToImageMagick;
}
protected boolean isUseOldStyleFfmpegMap() {
return "1".equals(configurationmanagement.getConfValue(
"use.old.style.ffmpeg.map.option", String.class, "0"));
}
protected String getStreamFolderName() {
return getStreamFolderName("hibernate");
}
protected String getStreamFolderName(FlvRecording flvRecording) {
return getStreamFolderName("" + flvRecording.getRoom_id());
}
protected String getStreamFolderName(String name) {
String streamFolderName = ScopeApplicationAdapter.webAppPath
+ File.separatorChar + OpenmeetingsVariables.STREAMS_DIR + File.separatorChar
+ name + File.separatorChar;
log.debug("###################################################");
log.debug("### streamFolderName - " + streamFolderName);
File sf = new File(streamFolderName);
if (!sf.exists()) {
log.debug("### streamFolderName is NOT exists");
if (!sf.mkdir()) {
log.error("### streamFolderName: Failed to create folder");
}
}
return streamFolderName;
}
protected void deleteFileIfExists(String name) {
File f = new File(name);
if (f.exists()) {
f.delete();
}
}
protected String[] mergeAudioToWaves(List<String> listOfFullWaveFiles, String outputFullWav) throws Exception {
String[] argv_full_sox = new String[listOfFullWaveFiles.size() + 3];
log.debug(" listOfFullWaveFiles "+listOfFullWaveFiles.size()+" argv_full_sox LENGTH "+argv_full_sox.length);
argv_full_sox[0] = getPathToSoX();
argv_full_sox[1] = "-m";
int i = 0;
for (;i < listOfFullWaveFiles.size(); i++) {
log.debug(" i "+i+" = "+listOfFullWaveFiles.get(i));
argv_full_sox[2 + i] = listOfFullWaveFiles.get(i);
}
log.debug(" i + 2 "+(i+2)+" "+outputFullWav);
argv_full_sox[i + 2] = outputFullWav;
return argv_full_sox;
}
protected void stripAudioFirstPass(FlvRecording flvRecording,
List<HashMap<String, String>> returnLog,
List<String> listOfFullWaveFiles, String streamFolderName) throws Exception {
List<FlvRecordingMetaData> metaDataList = flvRecordingMetaDataDaoImpl
.getFlvRecordingMetaDataAudioFlvsByRecording(flvRecording
.getFlvRecordingId());
stripAudioFirstPass(flvRecording, returnLog, listOfFullWaveFiles, streamFolderName, metaDataList);
}
protected void stripAudioFirstPass(FlvRecording flvRecording,
List<HashMap<String, String>> returnLog,
List<String> listOfFullWaveFiles, String streamFolderName,
List<FlvRecordingMetaData> metaDataList) {
try {
// Init variables
log.debug("### meta Data Number - " + metaDataList.size());
log.debug("###################################################");
for (FlvRecordingMetaData flvRecordingMetaData : metaDataList) {
if (flvRecordingMetaData.getStreamReaderThreadComplete() == null) {
throw new IllegalStateException("StreamReaderThreadComplete Bit is NULL, error in recording");
}
if (!flvRecordingMetaData.getStreamReaderThreadComplete()) {
log.debug("### meta Stream not yet written to disk" + flvRecordingMetaData.getFlvRecordingMetaDataId());
boolean doStop = true;
while(doStop) {
log.debug("### Stream not yet written Thread Sleep - " + flvRecordingMetaData.getFlvRecordingMetaDataId());
flvRecordingMetaData = flvRecordingMetaDataDaoImpl.getFlvRecordingMetaDataById(flvRecordingMetaData.getFlvRecordingMetaDataId());
if (flvRecordingMetaData.getStreamReaderThreadComplete()) {
log.debug("### Stream now written Thread continue - " );
doStop = false;
}
Thread.sleep(100L);
}
}
String inputFlv = streamFolderName
+ flvRecordingMetaData.getStreamName() + ".flv";
String hashFileName = flvRecordingMetaData.getStreamName()
+ "_WAVE.wav";
String outputWav = streamFolderName + hashFileName;
flvRecordingMetaData.setWavAudioData(hashFileName);
File inputFlvFile = new File(inputFlv);
log.debug("FLV File Name: {} Length: {} ",inputFlvFile.getName(), inputFlvFile.length());
if (inputFlvFile.exists()) {
String[] argv = new String[] { this.getPathToFFMPEG(),
"-async", "1", "-i", inputFlv, outputWav };
log.debug("START stripAudioFromFLVs ################# ");
for (int i = 0; i < argv.length; i++) {
log.debug(" i " + i + " argv-i " + argv[i]);
}
log.debug("END stripAudioFromFLVs ################# ");
returnLog.add(ProcessHelper.executeScript("generateFFMPEG",
argv));
// check if the resulting Audio is valid
File output_wav = new File(outputWav);
if (!output_wav.exists()) {
flvRecordingMetaData.setAudioIsValid(false);
} else {
if (output_wav.length() == 0) {
flvRecordingMetaData.setAudioIsValid(false);
} else {
flvRecordingMetaData.setAudioIsValid(true);
}
}
} else {
flvRecordingMetaData.setAudioIsValid(false);
}
if (flvRecordingMetaData.getAudioIsValid()) {
// Strip Wave to Full Length
String outputGapFullWav = outputWav;
// Fix Start/End in Audio
List<FlvRecordingMetaDelta> flvRecordingMetaDeltas = flvRecordingMetaDeltaDaoImpl
.getFlvRecordingMetaDeltaByMetaId(flvRecordingMetaData
.getFlvRecordingMetaDataId());
int counter = 0;
for (FlvRecordingMetaDelta flvRecordingMetaDelta : flvRecordingMetaDeltas) {
String inputFile = outputGapFullWav;
// Strip Wave to Full Length
String hashFileGapsFullName = flvRecordingMetaData
.getStreamName()
+ "_GAP_FULL_WAVE_"
+ counter
+ ".wav";
outputGapFullWav = streamFolderName
+ hashFileGapsFullName;
flvRecordingMetaDelta
.setWaveOutPutName(hashFileGapsFullName);
String[] argv_sox = null;
if (flvRecordingMetaDelta.getIsStartPadding() != null
&& flvRecordingMetaDelta.getIsStartPadding()) {
double gapSeconds = Double.valueOf(
flvRecordingMetaDelta.getDeltaTime()
.toString()).doubleValue() / 1000;
Double.valueOf(
flvRecordingMetaDelta.getDeltaTime()
.toString()).doubleValue();
if (gapSeconds > 0) {
// Add the item at the beginning
argv_sox = new String[] { this.getPathToSoX(),
inputFile, outputGapFullWav, "pad",
String.valueOf(gapSeconds).toString(),
"0" };
}
} else if (flvRecordingMetaDelta.getIsEndPadding() != null
&& flvRecordingMetaDelta.getIsEndPadding()) {
double gapSeconds = Double.valueOf(
flvRecordingMetaDelta.getDeltaTime()
.toString()).doubleValue() / 1000;
if (gapSeconds > 0) {
// Add the item at the end
argv_sox = new String[] { this.getPathToSoX(),
inputFile, outputGapFullWav, "pad",
"0",
String.valueOf(gapSeconds).toString() };
}
}
if (argv_sox != null) {
log.debug("START addGapAudioToWaves ################# ");
log.debug("START addGapAudioToWaves ################# Delta-ID :: "
+ flvRecordingMetaDelta
.getFlvRecordingMetaDeltaId());
String commandHelper = " ";
for (int i = 0; i < argv_sox.length; i++) {
commandHelper += " " + argv_sox[i];
}
log.debug(" commandHelper " + commandHelper);
log.debug("END addGapAudioToWaves ################# ");
returnLog.add(ProcessHelper.executeScript("fillGap",
argv_sox));
this.flvRecordingMetaDeltaDaoImpl
.updateFlvRecordingMetaDelta(flvRecordingMetaDelta);
counter++;
} else {
outputGapFullWav = inputFile;
}
}
// Strip Wave to Full Length
String hashFileFullName = flvRecordingMetaData
.getStreamName() + "_FULL_WAVE.wav";
String outputFullWav = streamFolderName + hashFileFullName;
// Calculate delta at beginning
Long deltaTimeStartMilliSeconds = flvRecordingMetaData
.getRecordStart().getTime()
- flvRecording.getRecordStart().getTime();
Float startPadding = Float
.parseFloat(deltaTimeStartMilliSeconds.toString()) / 1000;
// Calculate delta at ending
Long deltaTimeEndMilliSeconds = flvRecording.getRecordEnd()
.getTime()
- flvRecordingMetaData.getRecordEnd().getTime();
Float endPadding = Float
.parseFloat(deltaTimeEndMilliSeconds.toString()) / 1000;
String[] argv_sox = new String[] { this.getPathToSoX(),
outputGapFullWav, outputFullWav, "pad",
startPadding.toString(), endPadding.toString() };
log.debug("START addAudioToWaves ################# ");
String padString = "";
for (int i = 0; i < argv_sox.length; i++) {
padString += " " + argv_sox[i];
}
log.debug("padString :: " + padString);
log.debug("END addAudioToWaves ################# ");
returnLog.add(ProcessHelper.executeScript(
"addStartEndToAudio", argv_sox));
// Fix for Audio Length - Invalid Audio Length in Recorded
// Files
// Audio must match 100% the Video
log.debug("############################################");
log.debug("Trim Audio to Full Length -- Start");
File aFile = new File(outputFullWav);
if (!aFile.exists()) {
throw new Exception(
"Audio File does not exist , could not extract the Audio correctly");
}
flvRecordingMetaData.setFullWavAudioData(hashFileFullName);
// Finally add it to the row!
listOfFullWaveFiles.add(outputFullWav);
}
flvRecordingMetaDataDaoImpl
.updateFlvRecordingMetaData(flvRecordingMetaData);
}
} catch (Exception err) {
log.error("[stripAudioFromFLVs]", err);
}
}
}