blob: 80b0464c9f64e867f5da67a6a1fe0330d6ecff87 [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.LinkedList;
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 {
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) {
try {
if (isUseOldStyleFfmpegMap()) {
FFMPEG_MAP_PARAM = ".";
}
FlvRecording flvRecording = recordingDao.get(flvRecordingId);
log.debug("flvRecording " + flvRecording.getFlvRecordingId());
// Strip Audio out of all Audio-FLVs
stripAudioFromFLVs(flvRecording);
// Add empty pieces at the beginning and end of the wav
} catch (Exception err) {
log.error("[startConversion]", err);
}
}
public void stripAudioFromFLVs(FlvRecording flvRecording) {
List<ConverterProcessResult> returnLog = new ArrayList<ConverterProcessResult>();
List<String> listOfFullWaveFiles = new LinkedList<String>();
File streamFolder = getStreamFolder(flvRecording);
try {
stripAudioFirstPass(flvRecording, returnLog, listOfFullWaveFiles, streamFolder);
// Merge Wave to Full Length
String streamFolderGeneralName = getStreamsHibernateDir().getCanonicalPath() + File.separator; // FIXME
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");
}
screenMetaData = waitForTheStream(screenMetaData.getFlvRecordingMetaDataId());
String hashFileFullName = screenMetaData.getStreamName() + "_FINAL_WAVE.wav";
String outputFullWav = new File(streamFolder, hashFileFullName).getCanonicalPath();
if (listOfFullWaveFiles.size() == 1) {
outputFullWav = listOfFullWaveFiles.get(0);
screenMetaData.setFullWavAudioData(hashFileFullName);
} else if (listOfFullWaveFiles.size() > 0) {
String[] argv_full_sox = mergeAudioToWaves(listOfFullWaveFiles, outputFullWav);
screenMetaData.setFullWavAudioData(hashFileFullName);
metaDataDao.update(screenMetaData);
returnLog.add(ProcessHelper.executeScript("mergeAudioToWaves", argv_full_sox));
} else {
// create default Audio to merge it.
// strip to content length
String outputWav = streamFolderGeneralName + "one_second.wav";
// Calculate delta at beginning
Long deltaTimeMilliSeconds = flvRecording.getRecordEnd().getTime()
- flvRecording.getRecordStart().getTime();
Float deltaPadding = (Float.parseFloat(deltaTimeMilliSeconds.toString()) / 1000) - 1;
String[] argv_full_sox = new String[] { getPathToSoX(), outputWav, outputFullWav, "pad", "0",
deltaPadding.toString() };
screenMetaData.setFullWavAudioData(hashFileFullName);
metaDataDao.update(screenMetaData);
returnLog.add(ProcessHelper.executeScript("generateSampleAudio", argv_full_sox));
}
// Merge Audio with Video / Calculate resulting FLV
String inputScreenFullFlv = new File(streamFolder, screenMetaData.getStreamName() + ".flv")
.getCanonicalPath();
String hashFileFullNameFlv = "flvRecording_" + flvRecording.getFlvRecordingId() + ".flv";
String outputFullFlv = streamFolderGeneralName + hashFileFullNameFlv;
// 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(), //
"-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 };
returnLog.add(ProcessHelper.executeScript("generateFullFLV", argv_fullFLV));
flvRecording.setFileHash(hashFileFullNameFlv);
// Extract first Image for preview purpose
// ffmpeg -i movie.flv -vcodec mjpeg -vframes 1 -an -f rawvideo -s
// 320x240 movie.jpg
String hashFileFullNameJPEG = "flvRecording_" + flvRecording.getFlvRecordingId() + ".jpg";
String outPutJpeg = streamFolderGeneralName + hashFileFullNameJPEG;
flvRecording.setPreviewImage(hashFileFullNameJPEG);
String[] argv_previewFLV = new String[] { //
getPathToFFMPEG(), //
"-i", outputFullFlv, //
"-vcodec", "mjpeg", //
"-vframes", "1", "-an", //
"-f", "rawvideo", //
"-s", flvWidth + "x" + flvHeight, //
outPutJpeg };
returnLog.add(ProcessHelper.executeScript("previewFullFLV", argv_previewFLV));
String alternateDownloadName = "flvRecording_" + flvRecording.getFlvRecordingId() + ".avi";
String alternateDownloadFullName = streamFolderGeneralName + alternateDownloadName;
String[] argv_alternateDownload = new String[] { getPathToFFMPEG(), "-i", outputFullFlv, "-vcodec",
"copy", alternateDownloadFullName };
returnLog.add(ProcessHelper.executeScript("alternateDownload", argv_alternateDownload));
flvRecording.setAlternateDownload(alternateDownloadName);
recordingDao.updateFlvRecording(flvRecording);
convertToMp4(flvRecording, returnLog);
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("[stripAudioFromFLVs]", err);
}
}
}