blob: da133e14928951c08349da74fe1854523b45382f [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.converter;
import static org.apache.openmeetings.util.OmFileHelper.getStreamsHibernateDir;
import static org.apache.openmeetings.util.OpenmeetingsVariables.webAppRootKey;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.apache.openmeetings.db.dao.record.FlvRecordingDao;
import org.apache.openmeetings.db.dao.record.FlvRecordingLogDao;
import org.apache.openmeetings.db.dao.record.FlvRecordingMetaDataDao;
import org.apache.openmeetings.db.entity.record.FlvRecording;
import org.apache.openmeetings.db.entity.record.FlvRecordingMetaData;
import org.apache.openmeetings.db.entity.record.FlvRecordingMetaData.Status;
import org.apache.openmeetings.util.process.ConverterProcessResult;
import org.apache.openmeetings.util.process.ProcessHelper;
import org.red5.logging.Red5LoggerFactory;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
public class FlvRecorderConverter extends BaseConverter implements IRecordingConverter {
private static final Logger log = Red5LoggerFactory.getLogger(FlvRecorderConverter.class, webAppRootKey);
// Spring loaded Beans
@Autowired
private FlvRecordingDao recordingDao;
@Autowired
private FlvRecordingMetaDataDao metaDataDao;
@Autowired
private FlvRecordingLogDao logDao;
private String FFMPEG_MAP_PARAM = ":";
public void startConversion(Long flvRecordingId) {
FlvRecording flvRecording = null;
try {
if (isUseOldStyleFfmpegMap()) {
FFMPEG_MAP_PARAM = ".";
}
String finalNamePrefix = "flvRecording_" + flvRecordingId;
flvRecording = recordingDao.get(flvRecordingId);
log.debug("flvRecording " + flvRecording.getFlvRecordingId());
List<ConverterProcessResult> returnLog = new ArrayList<ConverterProcessResult>();
List<String> listOfFullWaveFiles = new ArrayList<String>();
File streamFolder = getStreamFolder(flvRecording);
FlvRecordingMetaData screenMetaData = metaDataDao.getScreenMetaDataByRecording(flvRecording.getFlvRecordingId());
if (screenMetaData == null) {
throw new Exception("screenMetaData is Null FlvRecordingId " + flvRecording.getFlvRecordingId());
}
if (screenMetaData.getStreamStatus() == Status.NONE) {
throw new Exception("Stream has not been started, error in recording");
}
flvRecording.setStatus(FlvRecording.Status.CONVERTING);
flvRecording = recordingDao.update(flvRecording);
screenMetaData = waitForTheStream(screenMetaData.getFlvRecordingMetaDataId());
stripAudioFirstPass(flvRecording, returnLog, listOfFullWaveFiles, streamFolder);
// Merge Wave to Full Length
String hashFileFullName = screenMetaData.getStreamName() + "_FINAL_WAVE.wav";
String outputFullWav = new File(streamFolder, hashFileFullName).getCanonicalPath();
if (listOfFullWaveFiles.size() == 1) {
outputFullWav = listOfFullWaveFiles.get(0);
} else if (listOfFullWaveFiles.size() > 0) {
String[] argv_full_sox = mergeAudioToWaves(listOfFullWaveFiles, outputFullWav);
returnLog.add(ProcessHelper.executeScript("mergeAudioToWaves", argv_full_sox));
} else {
// create default Audio to merge it. strip to content length
String outputWav = new File(getStreamsHibernateDir(), "one_second.wav").getCanonicalPath();
// Calculate delta at beginning
double deltaPadding = diffSeconds(flvRecording.getRecordEnd(), flvRecording.getRecordStart());
String[] argv_full_sox = new String[] { getPathToSoX(), outputWav, outputFullWav, "pad", "0", "" + deltaPadding };
returnLog.add(ProcessHelper.executeScript("generateSampleAudio", argv_full_sox));
}
screenMetaData.setFullWavAudioData(hashFileFullName);
metaDataDao.update(screenMetaData);
// Merge Audio with Video / Calculate resulting FLV
String inputScreenFullFlv = new File(streamFolder, screenMetaData.getStreamName() + ".flv").getCanonicalPath();
File outputFullFlv = new File(getStreamsHibernateDir(), finalNamePrefix + ".flv");
// ffmpeg -vcodec flv -qscale 9.5 -r 25 -ar 22050 -ab 32k -s 320x240
// -i 65318fb5c54b1bc1b1bca077b493a914_28_12_2009_23_38_17_FINAL_WAVE.wav
// -i 65318fb5c54b1bc1b1bca077b493a914_28_12_2009_23_38_17.flv
// final1.flv
int flvWidth = flvRecording.getWidth();
int flvHeight = flvRecording.getHeight();
log.debug("flvWidth -1- " + flvWidth);
log.debug("flvHeight -1- " + flvHeight);
flvWidth = Double.valueOf((Math.floor(flvWidth / 16)) * 16).intValue();
flvHeight = Double.valueOf((Math.floor(flvHeight / 16)) * 16).intValue();
log.debug("flvWidth -2- " + flvWidth);
log.debug("flvHeight -2- " + flvHeight);
flvRecording.setFlvWidth(flvWidth);
flvRecording.setFlvHeight(flvHeight);
String[] argv_fullFLV = new String[] { getPathToFFMPEG(), "-y",//
"-itsoffset", formatMillis(diff(screenMetaData.getRecordStart(), flvRecording.getRecordStart())),
"-i", inputScreenFullFlv, "-i", outputFullWav, "-ar", "22050", //
"-acodec", "libmp3lame", //
"-ab", "32k", //
"-s", flvWidth + "x" + flvHeight, //
"-vcodec", "flashsv", //
"-map", "0" + FFMPEG_MAP_PARAM + "0", //
"-map", "1" + FFMPEG_MAP_PARAM + "0", //
outputFullFlv.getCanonicalPath() };
returnLog.add(ProcessHelper.executeScript("generateFullFLV", argv_fullFLV));
flvRecording.setFileHash(outputFullFlv.getName());
// Extract first Image for preview purpose
// ffmpeg -i movie.flv -vcodec mjpeg -vframes 1 -an -f rawvideo -s
// 320x240 movie.jpg
File outPutJpeg = new File(getStreamsHibernateDir(), finalNamePrefix + ".jpg");
flvRecording.setPreviewImage(outPutJpeg.getName());
String[] argv_previewFLV = new String[] { //
getPathToFFMPEG(), "-y",//
"-i", outputFullFlv.getCanonicalPath(), //
"-vcodec", "mjpeg", //
"-vframes", "1", "-an", //
"-f", "rawvideo", //
"-s", flvWidth + "x" + flvHeight, //
outPutJpeg.getCanonicalPath() };
returnLog.add(ProcessHelper.executeScript("previewFullFLV", argv_previewFLV));
File alternateDownload = new File(getStreamsHibernateDir(), finalNamePrefix + ".avi");
String[] argv_alternateDownload = new String[] { getPathToFFMPEG(), "-y", "-i", outputFullFlv.getCanonicalPath(), "-vcodec",
"copy", alternateDownload.getCanonicalPath() };
returnLog.add(ProcessHelper.executeScript("alternateDownload", argv_alternateDownload));
flvRecording.setAlternateDownload(alternateDownload.getName());
updateDuration(flvRecording);
convertToMp4(flvRecording, returnLog);
flvRecording.setStatus(FlvRecording.Status.PROCESSED);
logDao.deleteByRecordingId(flvRecording.getFlvRecordingId());
for (ConverterProcessResult returnMap : returnLog) {
logDao.addFLVRecordingLog("generateFFMPEG", flvRecording, returnMap);
}
// Delete Wave Files
for (String fileName : listOfFullWaveFiles) {
File audio = new File(fileName);
if (audio.exists()) {
audio.delete();
}
}
} catch (Exception err) {
log.error("[startConversion]", err);
flvRecording.setStatus(FlvRecording.Status.ERROR);
}
if (flvRecording != null) {
recordingDao.update(flvRecording);
}
}
}