package com.proudcase.util; import com.proudcase.constants.Constants; import com.proudcase.exclogger.ExceptionLogger; import com.proudcase.filehandling.PropertyReader; import com.proudcase.mongodb.manager.ManagerFactory; import com.proudcase.mongodb.manager.MessagesManager; import com.proudcase.mongodb.manager.VideoLinkManager; import com.proudcase.persistence.MessagesBean; import com.proudcase.persistence.UserBean; import com.proudcase.persistence.VideoLinkBean; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Date; import java.util.List; /** * Copyright © 03.07.2013 Michel Vocks This file is part of proudcase. * * proudcase is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * proudcase is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * proudcase. If not, see <http://www.gnu.org/licenses/>. * * @Author: Michel Vocks * * @Date: 17.10.2013 * * @Encoding: UTF-8 */ public class VideoEncoderUtil implements Runnable { // FFMPEG arguments private static final List<String> FFMPEG_ARGS = new ArrayList<>(); // windows start command private static final List<String> WINSTARTCOMMAND = new ArrayList<>(); // Thumbnail commands for ffmpeg private static final List<String> THUMBNAIL_FFMPEG_ARGS = new ArrayList<>(); // input placeholder private static final String INPUTPH = "#&INPUT"; // output placeholder private static final String OUTPUTPH = "#$OUTPUT"; static { // FFMPEG args FFMPEG_ARGS.add("ffmpeg"); FFMPEG_ARGS.add("-i"); // next arg is path to input video FFMPEG_ARGS.add(INPUTPH); // path to input video FFMPEG_ARGS.add("-movflags"); // relocate MOOV atom in the video FFMPEG_ARGS.add("faststart"); // allow playback to begin before the file is completely downloaded FFMPEG_ARGS.add("-c:v"); // next arg is the output format FFMPEG_ARGS.add("libx264"); // h.265 format conversion FFMPEG_ARGS.add(OUTPUTPH); // output video path // win start command WINSTARTCOMMAND.add("cmd"); WINSTARTCOMMAND.add("/C"); // thumbnail args for ffmpeg THUMBNAIL_FFMPEG_ARGS.add("ffmpeg"); THUMBNAIL_FFMPEG_ARGS.add("-i"); // next arg is path to input video THUMBNAIL_FFMPEG_ARGS.add(INPUTPH); // path to input video THUMBNAIL_FFMPEG_ARGS.add("-r"); // force the frames per second THUMBNAIL_FFMPEG_ARGS.add("1"); // one frame per second THUMBNAIL_FFMPEG_ARGS.add("-s"); // force to capture in a specific quality THUMBNAIL_FFMPEG_ARGS.add("hd1080"); // hd1080 == 1920x1080 resolution THUMBNAIL_FFMPEG_ARGS.add("-t"); // force to capture only specified seconds THUMBNAIL_FFMPEG_ARGS.add("1"); // forse to capture only for one second THUMBNAIL_FFMPEG_ARGS.add("-ss"); // force to start at a specific point THUMBNAIL_FFMPEG_ARGS.add("00:00:10"); // capture start after 10 seconds THUMBNAIL_FFMPEG_ARGS.add(OUTPUTPH); // output thumbnail path } ; // This property returns the name of the os private static final String OSPROP = "os.name"; // operating system abbreveations private static final String WINABB = "win"; // Some things we need for the encoding process private final String videoInputPath; private final String videoOutputPath; private final File logOutput; private final UserBean userObj; private final String messageBundle; public VideoEncoderUtil(String videoInputPath, String videoOutputPath, File logOutput, UserBean userObj, String messageBundle) { this.videoInputPath = videoInputPath; this.videoOutputPath = videoOutputPath; this.logOutput = logOutput; this.userObj = userObj; this.messageBundle = messageBundle; } /** * Starts external program "FFMPEG" which converts a video to other formats */ @Override public void run() { // Get the os String operatingSystem = System.getProperty(OSPROP).toLowerCase(); List<String> executeCommandEnc = new ArrayList<>(); List<String> executeCommandThumb = new ArrayList<>(); // is the current operating system windows? if (operatingSystem.indexOf(WINABB) >= 0) { // format the run command for capturing the thumbnail executeCommandThumb.addAll(WINSTARTCOMMAND); executeCommandThumb.addAll(THUMBNAIL_FFMPEG_ARGS); // format the run command for encoding executeCommandEnc.addAll(WINSTARTCOMMAND); executeCommandEnc.addAll(FFMPEG_ARGS); } else { // just add args executeCommandThumb.addAll(THUMBNAIL_FFMPEG_ARGS); executeCommandEnc.addAll(FFMPEG_ARGS); } // replace the input- and output placeholders with the real path for thumbnail capturing int index = executeCommandThumb.indexOf(INPUTPH); executeCommandThumb.remove(index); executeCommandThumb.add(index, videoInputPath); index = executeCommandThumb.indexOf(OUTPUTPH); executeCommandThumb.remove(index); // Just take the video path and add jpeg suffix executeCommandThumb.add(index, videoOutputPath + Constants.JPEG_SUFFIX); // replace the input- and output placeholders with the real path for encoding index = executeCommandEnc.indexOf(INPUTPH); executeCommandEnc.remove(index); executeCommandEnc.add(index, videoInputPath); index = executeCommandEnc.indexOf(OUTPUTPH); executeCommandEnc.remove(index); executeCommandEnc.add(index, videoOutputPath); // Get the file from the input video File tempVideoFile = new File(videoInputPath); try { // create a processbuilder which executes the thumbnail command ProcessBuilder processBuilder = new ProcessBuilder(executeCommandThumb); // Redirect all output to our log file // *IMPORTANT* remove this and the thread will get a deadlock! processBuilder.redirectInput(logOutput); processBuilder.redirectError(logOutput); processBuilder.redirectOutput(logOutput); // let's execute the thumbnail process Process ffmpegProcess = processBuilder.start(); // Wait until the capturing process is finished try { ffmpegProcess.waitFor(); } catch (InterruptedException ex) { throw new ExceptionLogger(ex, "Wait for ffmpeg thumbnail process interrupt exception!"); } // create a processbuilder which executes the encoding command processBuilder = new ProcessBuilder(executeCommandEnc); // Redirect all output to our log file // *IMPORTANT* remove this and the thread will get a deadlock! processBuilder.redirectInput(logOutput); processBuilder.redirectError(logOutput); processBuilder.redirectOutput(logOutput); // let's execute the encoding process ffmpegProcess = processBuilder.start(); // Wait until the encoding process is finished int exitResult = -100; try { exitResult = ffmpegProcess.waitFor(); } catch (InterruptedException ex) { throw new ExceptionLogger(ex, "Wait for ffmpeg encoding process interrupt exception!"); } // create a file object from the output file File encodedVideoFile = new File(videoOutputPath); // if we are here, then the encoding is hopefully done if (exitResult == 0 && encodedVideoFile.isFile()) { // first of all, delete the temp video file! if (tempVideoFile.isFile()) { tempVideoFile.delete(); } // Get the videoLink object from the database VideoLinkManager videoLinkManager = ManagerFactory.createVideoLinkManager(); VideoLinkBean videoLink = videoLinkManager.getVideoLinkByVideoName(encodedVideoFile.getName()); // this should never happen, just to be sure if (videoLink != null) { // mark that the encoding is done videoLink.setEncodingDone(true); // update the obj in the db videoLinkManager.save(videoLink); } // create a new message for the user MessagesBean finishMessage = new MessagesBean(); // set some attributes finishMessage.setReceiver(userObj); finishMessage.setSenddate(new Date()); // Get the message for the user String message = PropertyReader.getMessageResourceString( messageBundle, "videoencodingfinished", null, userObj.getPreferredLanguage()); // add the message finishMessage.setMessage(message); // finally, save our message obj in the db MessagesManager messagesManager = ManagerFactory.createMessagesManager(); messagesManager.save(finishMessage); } else { throw new ExceptionLogger(new InterruptedException(), "FFMPEG exit with error!"); } } catch (IOException ex) { try { throw new ExceptionLogger(ex, "Video encoding via ffmpeg not possible! (check ffmpeg in path environment)"); } catch (ExceptionLogger ex2) { } } catch (ExceptionLogger ex2) { } } }