Java tutorial
/* * 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.ArrayList; import java.util.LinkedList; import java.util.List; import org.apache.commons.transaction.util.FileHelper; import org.apache.openmeetings.OpenmeetingsVariables; import org.apache.openmeetings.data.flvrecord.FlvRecordingDao; import org.apache.openmeetings.data.flvrecord.FlvRecordingLogDao; import org.apache.openmeetings.data.flvrecord.FlvRecordingMetaDataDao; import org.apache.openmeetings.documents.GenerateThumbs; 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.utils.ProcessHelper; import org.red5.logging.Red5LoggerFactory; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; public class FlvInterviewConverter extends BaseConverter { private int leftSideLoud = 1; private int rightSideLoud = 1; private Integer leftSideTime = 0; private Integer rightSideTime = 0; private static final Logger log = Red5LoggerFactory.getLogger(FlvInterviewConverter.class, OpenmeetingsVariables.webAppRootKey); // Spring loaded Beans @Autowired private FlvRecordingDao flvRecordingDaoImpl = null; @Autowired private FlvRecordingMetaDataDao flvRecordingMetaDataDaoImpl = null; @Autowired private FlvRecordingLogDao flvRecordingLogDaoImpl; @Autowired private GenerateThumbs generateThumbs; public void startReConversion(Long flvRecordingId, Integer leftSideLoud, Integer rightSideLoud, Integer leftSideTime, Integer rightSideTime) { log.debug("++++++++++++ leftSideLoud :: " + leftSideLoud); log.debug("++++++++++++ rightSideLoud :: " + rightSideLoud); this.leftSideLoud += leftSideLoud; this.rightSideLoud += rightSideLoud; this.leftSideTime = leftSideTime; this.rightSideTime = rightSideTime; log.debug("++++++++++++ this.leftSideLoud :: " + this.leftSideLoud); log.debug("++++++++++++ this.rightSideLoud :: " + this.rightSideLoud); log.debug("++++++++++++ this.leftSideTime :: " + this.leftSideTime); log.debug("++++++++++++ this.rightSideTime :: " + this.rightSideTime); startConversion(flvRecordingId, true); } public void startConversion(Long flvRecordingId) { startConversion(flvRecordingId, false); } public void startConversion(Long flvRecordingId, boolean reconversion) { try { FlvRecording flvRecording = this.flvRecordingDaoImpl.getFlvRecordingById(flvRecordingId); log.debug("flvRecording " + flvRecording.getFlvRecordingId()); // Strip Audio out of all Audio-FLVs stripAudioFromFLVs(flvRecording, reconversion); // Add empty pieces at the beginning and end of the wav } catch (Exception err) { log.error("[startConversion]", err); } } private String[] mergeAudioToWaves(List<String> listOfFullWaveFiles, String outputFullWav, List<FlvRecordingMetaData> metaDataList) { String[] argv_full_sox = new String[listOfFullWaveFiles.size() + 5]; argv_full_sox[0] = this.getPathToSoX(); argv_full_sox[1] = "-m"; int counter = 2; for (int i = 0; i < listOfFullWaveFiles.size(); i++) { for (FlvRecordingMetaData flvRecordingMetaData : metaDataList) { String hashFileFullNameStored = flvRecordingMetaData.getFullWavAudioData(); String fullFilePath = listOfFullWaveFiles.get(i); String fileNameOnly = new File(fullFilePath).getName(); if (hashFileFullNameStored.equals(fileNameOnly)) { if (flvRecordingMetaData.getInteriewPodId() == 1) { argv_full_sox[counter] = "-v " + this.leftSideLoud; counter++; } if (flvRecordingMetaData.getInteriewPodId() == 2) { argv_full_sox[counter] = "-v " + this.rightSideLoud; counter++; } } } argv_full_sox[counter] = listOfFullWaveFiles.get(i); counter++; } argv_full_sox[counter] = outputFullWav; return argv_full_sox; } public void stripAudioFromFLVs(FlvRecording flvRecording, boolean reconversion) { List<ConverterProcessResult> returnLog = new ArrayList<ConverterProcessResult>(); List<String> listOfFullWaveFiles = new LinkedList<String>(); File streamFolder = getStreamFolder(flvRecording); List<FlvRecordingMetaData> metaDataList = flvRecordingMetaDataDaoImpl .getFlvRecordingMetaDataAudioFlvsByRecording(flvRecording.getFlvRecordingId()); stripAudioFirstPass(flvRecording, returnLog, listOfFullWaveFiles, streamFolder, metaDataList); try { // Merge Wave to Full Length File streamFolderGeneral = getStreamFolder(); String hashFileFullName = "INTERVIEW_" + flvRecording.getFlvRecordingId() + "_FINAL_WAVE.wav"; String outputFullWav = streamFolder.getAbsolutePath() + File.separatorChar + hashFileFullName; deleteFileIfExists(outputFullWav); if (listOfFullWaveFiles.size() == 1) { outputFullWav = listOfFullWaveFiles.get(0); } else if (listOfFullWaveFiles.size() > 0) { String[] argv_full_sox; if (reconversion) { argv_full_sox = mergeAudioToWaves(listOfFullWaveFiles, outputFullWav, metaDataList); } else { argv_full_sox = mergeAudioToWaves(listOfFullWaveFiles, outputFullWav); } log.debug("START mergeAudioToWaves ################# "); log.debug(argv_full_sox.toString()); String iString = ""; for (int i = 0; i < argv_full_sox.length; i++) { iString += argv_full_sox[i] + " "; } log.debug(iString); log.debug("END mergeAudioToWaves ################# "); returnLog.add(ProcessHelper.executeScript("mergeWave", argv_full_sox)); } else { // create default Audio to merge it. // strip to content length File outputWav = new File(streamFolderGeneral, "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[] { this.getPathToSoX(), outputWav.getCanonicalPath(), outputFullWav, "pad", "0", deltaPadding.toString() }; log.debug("START generateSampleAudio ################# "); String tString = ""; for (int i = 0; i < argv_full_sox.length; i++) { tString += argv_full_sox[i] + " "; } log.debug(tString); log.debug("END generateSampleAudio ################# "); returnLog.add(ProcessHelper.executeScript("mergeWave", argv_full_sox)); } // Merge Audio with Video / Calculate resulting FLV // Start extracting image sequence int frameRate = 25; for (FlvRecordingMetaData flvRecordingMetaData : metaDataList) { // FLV to 24 FPS Sequence AVI String inputFlv = new File(streamFolder, flvRecordingMetaData.getStreamName() + ".flv") .getCanonicalPath(); File inputFlvFile = new File(inputFlv); if (inputFlvFile.exists()) { // TO Image Sequence // Image Folder File imageSequenceFolder = new File(streamFolder, "" + flvRecordingMetaData.getFlvRecordingMetaDataId()); imageSequenceFolder.mkdir(); String outputImages = new File(imageSequenceFolder, "image%d.png").getCanonicalPath(); String[] argv_imageSeq = new String[] { this.getPathToFFMPEG(), "-i", inputFlv, "-r", "" + frameRate, "-s", "320x240", outputImages }; log.debug("START generateImageSequence ################# "); String iString = ""; for (int i = 0; i < argv_imageSeq.length; i++) { iString += argv_imageSeq[i] + " "; } log.debug(iString); log.debug("END generateImageSequence ################# "); returnLog.add(ProcessHelper.executeScript("generateImageSequence", argv_imageSeq)); } } // Default Image for empty interview video pods File defaultInterviewImageFile = new File(streamFolderGeneral, "default_interview_image.png"); if (!defaultInterviewImageFile.exists()) { throw new Exception("defaultInterviewImageFile does not exist!"); } String defaultInterviewImage = defaultInterviewImageFile.getCanonicalPath(); // Create Folder for the output Image Sequence String outputImageMergedData = streamFolder.getAbsolutePath() + File.separatorChar + "INTERVIEW_" + flvRecording.getFlvRecordingId() + File.separatorChar; // Merged Image Folder File outputImageMergedDateFolder = new File(outputImageMergedData); outputImageMergedDateFolder.mkdir(); // Generate the Single Image by sequencing boolean jobRunning = true; long currentTimeInMilliSeconds = 0; long completeLengthInSeconds = flvRecording.getRecordEnd().getTime() - flvRecording.getRecordStart().getTime(); log.debug("completeLengthInSeconds :: " + completeLengthInSeconds); int sequenceCounter = 0; while (jobRunning) { // Process one Second of Movie String[] interviewPod1Images = new String[frameRate]; String[] interviewPod2Images = new String[frameRate]; int[] outputFrameNumbers = new int[frameRate]; for (FlvRecordingMetaData flvRecordingMetaData : metaDataList) { long deltaStartRecording = flvRecordingMetaData.getRecordStart().getTime() - flvRecording.getRecordStart().getTime(); if (flvRecording.getRecordStart().getTime() + currentTimeInMilliSeconds >= flvRecordingMetaData .getRecordStart().getTime() && flvRecording.getRecordStart().getTime() + currentTimeInMilliSeconds <= flvRecordingMetaData.getRecordEnd().getTime()) { // Calculate which images should be in here // Calculate the relative starting point long thisImageSequenceStartingPoint = currentTimeInMilliSeconds - deltaStartRecording; // Calculate the first and following frameRate FPS // Number int secondToStart = Long.valueOf(thisImageSequenceStartingPoint / 1000).intValue(); int firstFrame = secondToStart * frameRate; for (int i = 0; i < frameRate; i++) { int currentImageNumber = firstFrame + i; currentImageNumber -= (frameRate / 2); // Remove the first half seconds and fill // it up with black screens // Remove the first period of Images, this is where // the user has started // to share his Video but does not have agreed in // the Flash Security Warning Dialogue Integer initialGapSeconds = flvRecordingMetaData.getInitialGapSeconds(); if (initialGapSeconds != null) { int initialMissingImages = Double .valueOf(Math.floor((initialGapSeconds / 1000) * frameRate)).intValue(); currentImageNumber -= initialMissingImages; } String imageName = "image" + currentImageNumber + ".png"; File outputMetaImageFullDataFile = new File(streamFolder, "" + flvRecordingMetaData.getFlvRecordingMetaDataId() + File.separatorChar + imageName); String outputMetaImageFullData = !outputMetaImageFullDataFile.exists() ? defaultInterviewImage : outputMetaImageFullDataFile.getCanonicalPath(); if (flvRecordingMetaData.getInteriewPodId() == 1) { interviewPod1Images[i] = outputMetaImageFullData; } else if (flvRecordingMetaData.getInteriewPodId() == 2) { interviewPod2Images[i] = outputMetaImageFullData; } } } } // Update Sequence Count for (int i = 0; i < frameRate; i++) { outputFrameNumbers[i] = sequenceCounter; sequenceCounter++; } // Now we should have found the needed Images to calculate, in // case not we add an empty black screen for (int i = 0; i < frameRate; i++) { String addZeros = ""; String outputImageName = outputImageMergedData + "image" + addZeros + outputFrameNumbers[i] + ".png"; if (interviewPod1Images[i] == null) { interviewPod1Images[i] = defaultInterviewImage; } if (interviewPod2Images[i] == null) { interviewPod2Images[i] = defaultInterviewImage; } if (System.getProperty("os.name").toUpperCase().indexOf("WINDOWS") == -1) { String[] argv_imageMagick = new String[] { this.getPathToImageMagick(), "+append", interviewPod1Images[i], interviewPod2Images[i], outputImageName }; returnLog.add(ProcessHelper.executeScript("generateImageSequence", argv_imageMagick)); } else { returnLog.add(processImageWindows(interviewPod1Images[i], interviewPod2Images[i], outputImageName)); } } currentTimeInMilliSeconds += 1000; double cLength = 100 * ((double) currentTimeInMilliSeconds) / completeLengthInSeconds; int progress = Double.valueOf(cLength).intValue(); log.debug("completeLengthInSeconds|currentTimeInMilliSeconds " + completeLengthInSeconds + "|" + currentTimeInMilliSeconds + "|" + progress + "|" + cLength); flvRecordingDaoImpl.updateFlvRecordingProgress(flvRecording.getFlvRecordingId(), progress); if (currentTimeInMilliSeconds >= completeLengthInSeconds) { jobRunning = false; } } // Generate Movie by sequence of Images String imagescomplete = outputImageMergedData + "image%d.png"; String[] argv_generatedMoview = null; String inputScreenFullFlv = streamFolder.getAbsolutePath() + File.separatorChar + "COMPLETE_INTERVIEW_" + flvRecording.getFlvRecordingId() + ".flv"; deleteFileIfExists(inputScreenFullFlv); argv_generatedMoview = new String[] { this.getPathToFFMPEG(), "-i", imagescomplete, "-r", "" + frameRate, "-vcodec", "flv", "-qmax", "1", "-qmin", "1", inputScreenFullFlv }; log.debug("START generateFullBySequenceFLV ################# "); String tString2 = ""; for (int i = 0; i < argv_generatedMoview.length; i++) { tString2 += argv_generatedMoview[i] + " "; } log.debug(tString2); log.debug("END generateFullBySequenceFLV ################# "); returnLog.add(ProcessHelper.executeScript("generateFullBySequenceFLV", argv_generatedMoview)); String hashFileFullNameFlv = "flvRecording_" + flvRecording.getFlvRecordingId() + ".flv"; String outputFullFlv = new File(streamFolderGeneral, hashFileFullNameFlv).getCanonicalPath(); deleteFileIfExists(outputFullFlv); // 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 = 640; int flvHeight = 240; flvRecording.setFlvWidth(flvWidth); flvRecording.setFlvHeight(flvHeight); String[] argv_fullFLV = new String[] { this.getPathToFFMPEG(), // "-i", inputScreenFullFlv, "-i", outputFullWav, "-ar", "22050", // "-ab", "32k", // "-s", flvWidth + "x" + flvHeight, // "-vcodec", "flv", // "-r", "" + frameRate, "-qmax", "1", "-qmin", "1", outputFullFlv }; log.debug("START generateFullFLV ################# "); String tString = ""; for (int i = 0; i < argv_fullFLV.length; i++) { tString += argv_fullFLV[i] + " "; // log.debug(" i " + i + " argv-i " + argv_fullFLV[i]); } log.debug(tString); log.debug("END generateFullFLV ################# "); 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 = new File(streamFolderGeneral, hashFileFullNameJPEG).getCanonicalPath(); deleteFileIfExists(outPutJpeg); flvRecording.setPreviewImage(hashFileFullNameJPEG); String[] argv_previewFLV = new String[] { // this.getPathToFFMPEG(), // "-i", outputFullFlv, // "-vcodec", "mjpeg", // "-vframes", "100", "-an", // "-f", "rawvideo", // "-s", flvWidth + "x" + flvHeight, // outPutJpeg }; log.debug("START previewFullFLV ################# "); log.debug(argv_previewFLV.toString()); String kString = ""; for (int i = 0; i < argv_previewFLV.length; i++) { kString += argv_previewFLV[i] + " "; } log.debug(kString); log.debug("END previewFullFLV ################# "); returnLog.add(ProcessHelper.executeScript("generateFullFLV", argv_previewFLV)); String alternateDownloadName = "flvRecording_" + flvRecording.getFlvRecordingId() + ".avi"; String alternateDownloadFullName = new File(streamFolderGeneral, alternateDownloadName) .getCanonicalPath(); deleteFileIfExists(alternateDownloadFullName); String[] argv_alternateDownload = new String[] { this.getPathToFFMPEG(), "-i", outputFullFlv, alternateDownloadFullName }; log.debug("START alternateDownLoad ################# "); log.debug(argv_previewFLV.toString()); String sString = ""; for (int i = 0; i < argv_alternateDownload.length; i++) { sString += argv_alternateDownload[i] + " "; } log.debug(sString); log.debug("END alternateDownLoad ################# "); returnLog.add(ProcessHelper.executeScript("alternateDownload", argv_alternateDownload)); flvRecording.setAlternateDownload(alternateDownloadName); flvRecordingDaoImpl.updateFlvRecording(flvRecording); flvRecordingLogDaoImpl.deleteFLVRecordingLogByRecordingId(flvRecording.getFlvRecordingId()); for (ConverterProcessResult returnMap : returnLog) { flvRecordingLogDaoImpl.addFLVRecordingLog("generateFFMPEG", flvRecording, returnMap); } // Delete Wave Files for (String fileName : listOfFullWaveFiles) { File audio = new File(fileName); if (audio.exists()) { audio.delete(); } } // Delete all Image temp dirs for (FlvRecordingMetaData flvRecordingMetaData : metaDataList) { FileHelper.removeRec(new File(streamFolder, "" + flvRecordingMetaData.getFlvRecordingMetaDataId())); } FileHelper.removeRec(new File(outputImageMergedData)); } catch (Exception err) { log.error("[stripAudioFromFLVs]", err); } } public ConverterProcessResult thumbProcessImageWindows(String file1, String file2, String file3) { // Init variables String[] cmd = { this.getPathToImageMagick(), file1, file2, "+append", file3 }; return generateThumbs.processImageWindows(cmd); } public ConverterProcessResult processImageWindows(String file1, String file2, String file3) { return ProcessHelper.executeScriptWindows("processImageWindows", new String[] { getPathToImageMagick(), file1, file2, "+append", file3 }); } }