| /* |
| * 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); |
| } |
| } |
| } |