blob: 0efd4359e148ad5b19a01e28bf76ec3842f59a3b [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.apache.openmeetings.data.flvrecord.converter;
import java.io.File;
import java.util.List;
import org.apache.openmeetings.OpenmeetingsVariables;
import org.apache.openmeetings.data.basic.dao.ConfigurationDao;
import org.apache.openmeetings.data.flvrecord.FlvRecordingMetaDataDao;
import org.apache.openmeetings.data.flvrecord.FlvRecordingMetaDeltaDao;
import org.apache.openmeetings.documents.GenerateSWF;
import org.apache.openmeetings.documents.beans.ConverterProcessResult;
import org.apache.openmeetings.persistence.beans.flvrecord.FlvRecording;
import org.apache.openmeetings.persistence.beans.flvrecord.FlvRecordingMetaData;
import org.apache.openmeetings.persistence.beans.flvrecord.FlvRecordingMetaDelta;
import org.apache.openmeetings.utils.OmFileHelper;
import org.apache.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 ConfigurationDao configurationDao;
@Autowired
private FlvRecordingMetaDataDao flvRecordingMetaDataDaoImpl;
@Autowired
private FlvRecordingMetaDeltaDao flvRecordingMetaDeltaDaoImpl;
private String getPath(String key, String app) {
String path = configurationDao.getConfValue(key, String.class, "");
if (!path.equals("") && !path.endsWith(File.separator)) {
path += File.separator;
}
path += app;
return path;
}
protected String getPathToFFMPEG() {
return getPath("ffmpeg_path", "ffmpeg");
}
protected String getPathToSoX() {
return getPath("sox_path", "sox");
}
protected String getPathToImageMagick() {
return getPath("imagemagick_path", "convert") + GenerateSWF.execExt;
}
protected boolean isUseOldStyleFfmpegMap() {
return "1".equals(configurationDao.getConfValue(
"use.old.style.ffmpeg.map.option", String.class, "0"));
}
protected File getStreamFolder() {
return OmFileHelper.getStreamsHibernateDir();
}
protected File getStreamFolder(FlvRecording flvRecording) {
return OmFileHelper.getStreamsSubDir(flvRecording.getRoom_id());
}
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<ConverterProcessResult> returnLog,
List<String> listOfFullWaveFiles, File streamFolder) throws Exception {
List<FlvRecordingMetaData> metaDataList = flvRecordingMetaDataDaoImpl
.getFlvRecordingMetaDataAudioFlvsByRecording(flvRecording
.getFlvRecordingId());
stripAudioFirstPass(flvRecording, returnLog, listOfFullWaveFiles, streamFolder, metaDataList);
}
private String[] addSoxPad(List<ConverterProcessResult> returnLog, String job, double length, double position, String inFile, String outFile) {
if (length >= 0 && position >= 0 && (length > 0 || position > 0)) {
String[] argv_sox = new String[] { getPathToSoX(),
inFile, outFile, "pad",
"" + length, "" + position };
if (log.isDebugEnabled()) {
log.debug("START " + job + " ################# ");
String padString = "";
for (int i = 0; i < argv_sox.length; i++) {
padString += " " + argv_sox[i];
}
log.debug("padString :: " + padString);
log.debug("END " + job + " ################# ");
}
returnLog.add(ProcessHelper.executeScript(job, argv_sox));
return argv_sox;
} else {
log.debug("::addSoxPad " + job + " Invalid parameters: "
+ " length = " + length + "; position = " + position + "; inFile = " + inFile);
}
return null;
}
protected void stripAudioFirstPass(FlvRecording flvRecording,
List<ConverterProcessResult> returnLog,
List<String> listOfFullWaveFiles, File streamFolder,
List<FlvRecordingMetaData> metaDataList) {
try {
// Init variables
log.debug("### meta Data Number - " + metaDataList.size());
log.debug("###################################################");
for (FlvRecordingMetaData metaData : metaDataList) {
if (metaData.getStreamReaderThreadComplete() == null) {
throw new IllegalStateException("StreamReaderThreadComplete Bit is NULL, error in recording");
}
if (!metaData.getStreamReaderThreadComplete()) {
log.debug("### meta Stream not yet written to disk" + metaData.getFlvRecordingMetaDataId());
boolean doStop = true;
while(doStop) {
log.debug("### Stream not yet written Thread Sleep - " + metaData.getFlvRecordingMetaDataId());
metaData = flvRecordingMetaDataDaoImpl.getFlvRecordingMetaDataById(metaData.getFlvRecordingMetaDataId());
if (metaData.getStreamReaderThreadComplete()) {
log.debug("### Stream now written Thread continue - " );
doStop = false;
}
Thread.sleep(100L);
}
}
File inputFlvFile = new File(streamFolder, metaData.getStreamName() + ".flv");
String hashFileName = metaData.getStreamName() + "_WAVE.wav";
String outputWav = new File(streamFolder, hashFileName).getCanonicalPath(); //FIXME
metaData.setWavAudioData(hashFileName);
log.debug("FLV File Name: {} Length: {} ", inputFlvFile.getName(), inputFlvFile.length());
metaData.setAudioIsValid(false);
if (inputFlvFile.exists()) {
String[] argv = new String[] { this.getPathToFFMPEG(),
"-async", "1", "-i", inputFlvFile.getCanonicalPath(), 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() && output_wav.length() != 0) {
metaData.setAudioIsValid(true);
}
}
if (metaData.getAudioIsValid()) {
// Strip Wave to Full Length
String outputGapFullWav = outputWav;
// Fix Start/End in Audio
List<FlvRecordingMetaDelta> flvRecordingMetaDeltas = flvRecordingMetaDeltaDaoImpl
.getFlvRecordingMetaDeltaByMetaId(metaData
.getFlvRecordingMetaDataId());
int counter = 0;
for (FlvRecordingMetaDelta metaDelta : flvRecordingMetaDeltas) {
String inputFile = outputGapFullWav;
// Strip Wave to Full Length
String hashFileGapsFullName = metaData
.getStreamName()
+ "_GAP_FULL_WAVE_"
+ counter
+ ".wav";
outputGapFullWav = new File(streamFolder, hashFileGapsFullName).getCanonicalPath();
metaDelta.setWaveOutPutName(hashFileGapsFullName);
String[] argv_sox = null;
if (metaDelta.getDeltaTime() != null) {
if (metaDelta.getIsStartPadding() != null && metaDelta.getIsStartPadding()) {
double gapSeconds = ((double)metaDelta.getDeltaTime()) / 1000;
argv_sox = addSoxPad(returnLog, "fillGap", gapSeconds, 0, inputFile, outputGapFullWav);
} else if (metaDelta.getIsEndPadding() != null && metaDelta.getIsEndPadding()) {
double gapSeconds = ((double)metaDelta.getDeltaTime()) / 1000;
argv_sox = addSoxPad(returnLog, "fillGap", 0, gapSeconds, inputFile, outputGapFullWav);
}
}
if (argv_sox != null) {
log.debug("START fillGap ################# Delta-ID :: "
+ metaDelta.getFlvRecordingMetaDeltaId());
flvRecordingMetaDeltaDaoImpl.updateFlvRecordingMetaDelta(metaDelta);
counter++;
} else {
outputGapFullWav = inputFile;
}
}
// Strip Wave to Full Length
String hashFileFullName = metaData.getStreamName() + "_FULL_WAVE.wav";
String outputFullWav = new File(streamFolder, hashFileFullName).getCanonicalPath();
// Calculate delta at beginning
long deltaTimeStartMilliSeconds = metaData.getRecordStart().getTime()
- flvRecording.getRecordStart().getTime();
double length = ((double)deltaTimeStartMilliSeconds) / 1000;
// Calculate delta at ending
long deltaTimeEndMilliSeconds = flvRecording.getRecordEnd().getTime()
- metaData.getRecordEnd().getTime();
double endPadding = ((double)deltaTimeEndMilliSeconds) / 1000;
addSoxPad(returnLog, "addStartEndToAudio", length, endPadding, outputGapFullWav, outputFullWav);
// 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");
}
metaData.setFullWavAudioData(hashFileFullName);
// Finally add it to the row!
listOfFullWaveFiles.add(outputFullWav);
}
flvRecordingMetaDataDaoImpl.updateFlvRecordingMetaData(metaData);
}
} catch (Exception err) {
log.error("[stripAudioFromFLVs]", err);
}
}
}