Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* JAVE - A Java Audio/Video Encoder (based on FFMPEG)
*
* Copyright (C) 2008-2009 Carlo Pelliccia (www.sauronsoftware.it)
*
* This program 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.
*
* This program 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 this program. If not, see .
*/
package ws.schild.jave;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Main class of the package. Instances can encode audio and video streams.
*
* @author Carlo Pelliccia
*/
public class Encoder {
private final static Log LOG = LogFactory.getLog(Encoder.class);
/**
* This regexp is used to parse the ffmpeg output about the supported
* formats.
*/
private static final Pattern FORMAT_PATTERN = Pattern
.compile("^\\s*([D ])([E ])\\s+([\\w,]+)\\s+.+$");
/**
* This regexp is used to parse the ffmpeg output about the included
* encoders/decoders.
*/
private static final Pattern ENCODER_DECODER_PATTERN = Pattern.compile(
"^\\s*([AVS]).{5}\\s(\\S+).(.+)$", Pattern.CASE_INSENSITIVE);
/**
* This regexp is used to parse the ffmpeg output about the success of an
* encoding operation.
*/
private static final Pattern SUCCESS_PATTERN = Pattern.compile(
"^\\s*video\\:\\S+\\s+audio\\:\\S+\\s+subtitle\\:\\S+\\s+global headers\\:\\S+.*$",
Pattern.CASE_INSENSITIVE);
/**
* The locator of the ffmpeg executable used by this encoder.
*/
private final FFMPEGLocator locator;
/**
* It builds an encoder using a {@link DefaultFFMPEGLocator} instance to
* locate the ffmpeg executable to use.
*/
public Encoder() {
this.locator = new DefaultFFMPEGLocator();
}
/**
* It builds an encoder with a custom {@link FFMPEGLocator}.
*
* @param locator The locator picking up the ffmpeg executable used by the
* encoder.
*/
public Encoder(FFMPEGLocator locator) {
this.locator = locator;
}
/**
* Returns a list with the names of all the audio decoders bundled with the
* ffmpeg distribution in use. An audio stream can be decoded only if a
* decoder for its format is available.
*
* @return A list with the names of all the included audio decoders.
* @throws EncoderException If a problem occurs calling the underlying
* ffmpeg executable.
*/
public String[] getAudioDecoders() throws EncoderException {
return getCoders(false, true);
}
/**
* Returns a list with the names of all the audio encoders bundled with the
* ffmpeg distribution in use. An audio stream can be encoded using one of
* these encoders.
*
* @return A list with the names of all the included audio encoders.
* @throws EncoderException If a problem occurs calling the underlying
* ffmpeg executable.
*/
public String[] getAudioEncoders() throws EncoderException {
return getCoders(true, true);
}
/**
* Returns a list with the names of all the coders bundled with the ffmpeg
* distribution in use.
*
* @param encoder Do search encoders, else decoders
* @param audio Do search for audio encodes, else video
* @return A list with the names of all the included encoders
* @throws EncoderException If a problem occurs calling the underlying
* ffmpeg executable.
*/
protected String[] getCoders(boolean encoder, boolean audio) throws EncoderException {
ArrayList res = new ArrayList<>();
FFMPEGExecutor ffmpeg = locator.createExecutor();
ffmpeg.addArgument(encoder ? "-encoders" : "-decoders");
try
{
ffmpeg.execute();
RBufferedReader reader = null;
reader = new RBufferedReader(new InputStreamReader(ffmpeg
.getInputStream()));
String line;
String format = audio ? "A" : "V";
boolean headerFound = false;
boolean evaluateLine = false;
while ((line = reader.readLine()) != null)
{
if (line.trim().length() == 0)
{
continue;
}
if (headerFound)
{
if (evaluateLine)
{
Matcher matcher = ENCODER_DECODER_PATTERN.matcher(line);
if (matcher.matches())
{
//String encoderFlag = matcher.group(2);
String audioVideoFlag = matcher.group(1);
if (format.equals(audioVideoFlag))
{
String name = matcher.group(2);
res.add(name);
}
} else
{
break;
}
} else
{
evaluateLine = line.trim().equals("------");
}
} else if (line.trim().equals(encoder ? "Encoders:" : "Decoders:"))
{
headerFound = true;
}
}
} catch (IOException e)
{
throw new EncoderException(e);
} finally
{
ffmpeg.destroy();
}
int size = res.size();
String[] ret = new String[size];
for (int i = 0; i < size; i++)
{
ret[i] = res.get(i);
}
return ret;
}
/**
* Returns a list with the names of all the video decoders bundled with the
* ffmpeg distribution in use. A video stream can be decoded only if a
* decoder for its format is available.
*
* @return A list with the names of all the included video decoders.
* @throws EncoderException If a problem occurs calling the underlying
* ffmpeg executable.
*/
public String[] getVideoDecoders() throws EncoderException {
return getCoders(false, false);
}
/**
* Returns a list with the names of all the video encoders bundled with the
* ffmpeg distribution in use. A video stream can be encoded using one of
* these encoders.
*
* @return A list with the names of all the included video encoders.
* @throws EncoderException If a problem occurs calling the underlying
* ffmpeg executable.
*/
public String[] getVideoEncoders() throws EncoderException {
return getCoders(true, false);
}
/**
* Returns a list with the names of all the file formats supported at
* encoding time by the underlying ffmpeg distribution. A multimedia file
* could be encoded and generated only if the specified format is in this
* list.
*
* @return A list with the names of all the supported file formats at
* encoding time.
* @throws EncoderException If a problem occurs calling the underlying
* ffmpeg executable.
*/
public String[] getSupportedEncodingFormats() throws EncoderException {
return getSupportedCodingFormats(true);
}
/**
* Returns a list with the names of all the file formats supported at
* en/de-coding time by the underlying ffmpeg distribution.A multimedia file
* could be encoded and generated only if the specified format is in this
* list.
*
* @param encoding True for encoding job, false to decode a file
* @return A list with the names of all the supported file formats at
* encoding time.
* @throws EncoderException If a problem occurs calling the underlying
* ffmpeg executable.
*/
protected String[] getSupportedCodingFormats(boolean encoding) throws EncoderException {
ArrayList res = new ArrayList<>();
FFMPEGExecutor ffmpeg = locator.createExecutor();
ffmpeg.addArgument("-formats");
try
{
ffmpeg.execute();
RBufferedReader reader = null;
reader = new RBufferedReader(new InputStreamReader(ffmpeg
.getInputStream()));
String line;
String ed = encoding ? "E" : "D";
boolean headerFound = false;
boolean evaluateLine = false;
while ((line = reader.readLine()) != null)
{
if (line.trim().length() == 0)
{
continue;
}
if (headerFound)
{
if (evaluateLine)
{
Matcher matcher = FORMAT_PATTERN.matcher(line);
if (matcher.matches())
{
String encoderFlag = matcher.group(encoding ? 2 : 1);
if (ed.equals(encoderFlag))
{
String aux = matcher.group(3);
StringTokenizer st = new StringTokenizer(aux, ",");
while (st.hasMoreTokens())
{
String token = st.nextToken().trim();
if (!res.contains(token))
{
res.add(token);
}
}
}
} else
{
break;
}
} else
{
evaluateLine = line.trim().equals("--");
}
} else if (line.trim().equals("File formats:"))
{
headerFound = true;
}
}
} catch (IOException e)
{
throw new EncoderException(e);
} finally
{
ffmpeg.destroy();
}
int size = res.size();
String[] ret = new String[size];
for (int i = 0; i < size; i++)
{
ret[i] = res.get(i);
}
return ret;
}
/**
* Returns a list with the names of all the file formats supported at
* decoding time by the underlying ffmpeg distribution. A multimedia file
* could be open and decoded only if its format is in this list.
*
* @return A list with the names of all the supported file formats at
* decoding time.
* @throws EncoderException If a problem occurs calling the underlying
* ffmpeg executable.
*/
public String[] getSupportedDecodingFormats() throws EncoderException {
return getSupportedCodingFormats(false);
}
/**
* Re-encode a multimedia file.
*
* @param multimediaObject The source multimedia file. It cannot be null. Be
* sure this file can be decoded (see null null null null {@link Encoder#getSupportedDecodingFormats()},
* {@link Encoder#getAudioDecoders()} and
* {@link Encoder#getVideoDecoders()}).
* @param target The target multimedia re-encoded file. It cannot be null.
* If this file already exists, it will be overwrited.
* @param attributes A set of attributes for the encoding process.
* @throws IllegalArgumentException If both audio and video parameters are
* null.
* @throws InputFormatException If the source multimedia file cannot be
* decoded.
* @throws EncoderException If a problems occurs during the encoding
* process.
*/
public void encode(MultimediaObject multimediaObject, File target, EncodingAttributes attributes)
throws IllegalArgumentException, InputFormatException,
EncoderException {
encode(multimediaObject, target, attributes, null);
}
/**
* Re-encode a multimedia file.
*
* @param multimediaObject The source multimedia file. It cannot be null. Be
* sure this file can be decoded (see null null null null {@link Encoder#getSupportedDecodingFormats()},
* {@link Encoder#getAudioDecoders()} and
* {@link Encoder#getVideoDecoders()}).
* @param target The target multimedia re-encoded file. It cannot be null.
* If this file already exists, it will be overwrited.
* @param attributes A set of attributes for the encoding process.
* @param listener An optional progress listener for the encoding process.
* It can be null.
* @throws IllegalArgumentException If both audio and video parameters are
* null.
* @throws InputFormatException If the source multimedia file cannot be
* decoded.
* @throws EncoderException If a problems occurs during the encoding
* process.
*/
public void encode(MultimediaObject multimediaObject, File target, EncodingAttributes attributes,
EncoderProgressListener listener) throws IllegalArgumentException,
InputFormatException, EncoderException {
String formatAttribute = attributes.getFormat();
Float offsetAttribute = attributes.getOffset();
Float durationAttribute = attributes.getDuration();
AudioAttributes audioAttributes = attributes.getAudioAttributes();
VideoAttributes videoAttributes = attributes.getVideoAttributes();
if (audioAttributes == null && videoAttributes == null)
{
throw new IllegalArgumentException(
"Both audio and video attributes are null");
}
target = target.getAbsoluteFile();
target.getParentFile().mkdirs();
FFMPEGExecutor ffmpeg = locator.createExecutor();
if (offsetAttribute != null)
{
ffmpeg.addArgument("-ss");
ffmpeg.addArgument(String.valueOf(offsetAttribute.floatValue()));
}
ffmpeg.addArgument("-i");
ffmpeg.addArgument(multimediaObject.getFile().getAbsolutePath());
if (durationAttribute != null)
{
ffmpeg.addArgument("-t");
ffmpeg.addArgument(String.valueOf(durationAttribute.floatValue()));
}
if (videoAttributes == null)
{
ffmpeg.addArgument("-vn");
} else
{
String codec = videoAttributes.getCodec();
if (codec != null)
{
ffmpeg.addArgument("-vcodec");
ffmpeg.addArgument(codec);
}
String tag = videoAttributes.getTag();
if (tag != null)
{
ffmpeg.addArgument("-vtag");
ffmpeg.addArgument(tag);
}
Integer bitRate = videoAttributes.getBitRate();
if (bitRate != null)
{
ffmpeg.addArgument("-vb");
ffmpeg.addArgument(String.valueOf(bitRate.intValue()));
}
Integer frameRate = videoAttributes.getFrameRate();
if (frameRate != null)
{
ffmpeg.addArgument("-r");
ffmpeg.addArgument(String.valueOf(frameRate.intValue()));
}
VideoSize size = videoAttributes.getSize();
if (size != null)
{
ffmpeg.addArgument("-s");
ffmpeg.addArgument(String.valueOf(size.getWidth()) + "x"
+ String.valueOf(size.getHeight()));
}
if (videoAttributes.isFaststart())
{
ffmpeg.addArgument("-movflags");
ffmpeg.addArgument("faststart");
}
if (videoAttributes.getX264Profile() != null)
{
ffmpeg.addArgument("-profile:v");
ffmpeg.addArgument(videoAttributes.getX264Profile().getModeName());
}
if (videoAttributes.getVideoFilters().size() > 0)
{
for (VideoFilter videoFilter : videoAttributes.getVideoFilters())
{
ffmpeg.addArgument("-vf");
ffmpeg.addArgument(videoFilter.getExpression());
}
}
}
if (audioAttributes == null)
{
ffmpeg.addArgument("-an");
} else
{
String codec = audioAttributes.getCodec();
if (codec != null)
{
if (codec.equals("aac"))
{
codec = "libvo_aacenc";
}
ffmpeg.addArgument("-acodec");
ffmpeg.addArgument(codec);
}
Integer bitRate = audioAttributes.getBitRate();
if (bitRate != null)
{
ffmpeg.addArgument("-ab");
ffmpeg.addArgument(String.valueOf(bitRate.intValue()));
}
Integer channels = audioAttributes.getChannels();
if (channels != null)
{
ffmpeg.addArgument("-ac");
ffmpeg.addArgument(String.valueOf(channels.intValue()));
}
Integer samplingRate = audioAttributes.getSamplingRate();
if (samplingRate != null)
{
ffmpeg.addArgument("-ar");
ffmpeg.addArgument(String.valueOf(samplingRate.intValue()));
}
Integer volume = audioAttributes.getVolume();
if (volume != null)
{
ffmpeg.addArgument("-vol");
ffmpeg.addArgument(String.valueOf(volume.intValue()));
}
}
if (formatAttribute != null)
{
ffmpeg.addArgument("-f");
ffmpeg.addArgument(formatAttribute);
}
ffmpeg.addArgument("-y");
ffmpeg.addArgument(target.getAbsolutePath());
try
{
ffmpeg.execute();
} catch (IOException e)
{
throw new EncoderException(e);
}
try
{
String lastWarning = null;
long duration;
RBufferedReader reader = new RBufferedReader(
new InputStreamReader(ffmpeg.getErrorStream()));
MultimediaInfo info = multimediaObject.getInfo();
if (durationAttribute != null)
{
duration = (long) Math
.round((durationAttribute * 1000L));
} else
{
duration = info.getDuration();
if (offsetAttribute != null)
{
duration -= (long) Math
.round((offsetAttribute * 1000L));
}
}
if (listener != null)
{
listener.sourceInfo(info);
}
String line;
ConversionOutputAnalyzer outputAnalyzer= new ConversionOutputAnalyzer(duration, listener);
while ((line = reader.readLine()) != null)
{
outputAnalyzer.analyzeNewLine(line);
}
if (outputAnalyzer.getLastWarning() != null)
{
if (!SUCCESS_PATTERN.matcher(lastWarning).matches())
{
throw new EncoderException("No match for: " + SUCCESS_PATTERN + " in " + lastWarning);
}
}
} catch (IOException e)
{
throw new EncoderException(e);
} finally
{
ffmpeg.destroy();
}
}
}