use of org.opencastproject.composer.api.EncoderException in project opencast by opencast.
the class EncoderEngine method extract.
/**
* Extract several images from a video file.
*
* @param mediaSource
* File to extract images from
* @param format
* Encoding profile to use for extraction
* @param properties
* @param times
* Times at which to extract the images
* @return List of image files
* @throws EncoderException Something went wrong during image extraction
*/
List<File> extract(File mediaSource, EncodingProfile format, Map<String, String> properties, double... times) throws EncoderException {
List<File> extractedImages = new LinkedList<>();
try {
// Extract one image if no times are specified
if (times.length == 0) {
extractedImages.add(encode(mediaSource, format, properties));
}
for (double time : times) {
Map<String, String> params = new HashMap<>();
if (properties != null) {
params.putAll(properties);
}
DecimalFormatSymbols ffmpegFormat = new DecimalFormatSymbols();
ffmpegFormat.setDecimalSeparator('.');
DecimalFormat df = new DecimalFormat("0.00000", ffmpegFormat);
params.put("time", df.format(time));
extractedImages.add(encode(mediaSource, format, params));
}
} catch (Exception e) {
cleanup(extractedImages);
if (e instanceof EncoderException) {
throw (EncoderException) e;
} else {
throw new EncoderException("Image extraction failed", e);
}
}
return extractedImages;
}
use of org.opencastproject.composer.api.EncoderException in project opencast by opencast.
the class EncoderEngine method buildCommand.
/**
* Creates the command that is sent to the commandline encoder.
*
* @return the commandline
* @throws EncoderException
* in case of any error
*/
private List<String> buildCommand(final EncodingProfile profile, final Map<String, String> argumentReplacements) throws EncoderException {
List<String> command = new ArrayList<>();
command.add(binary);
command.add("-nostats");
String commandline = profile.getExtension(CMD_SUFFIX);
// ffmpeg.command.concatCmd = -i ...
for (String key : argumentReplacements.keySet()) {
if (key.startsWith(CMD_SUFFIX + '.')) {
final String shortKey = key.substring(CMD_SUFFIX.length() + 1);
commandline = commandline.replace("#{" + shortKey + "}", argumentReplacements.get(key));
}
}
String[] arguments;
try {
arguments = CommandLineUtils.translateCommandline(commandline);
} catch (Exception e) {
throw new EncoderException("Could not parse encoding profile command line", e);
}
for (String arg : arguments) {
String result = processParameters(arg, argumentReplacements);
if (StringUtils.isNotBlank(result)) {
command.add(result);
}
}
return command;
}
use of org.opencastproject.composer.api.EncoderException in project opencast by opencast.
the class EncoderEngine method process.
/**
* Executes the command line encoder with the given set of files and properties and using the provided encoding
* profile.
*
* @param source
* the source files for encoding
* @param profile
* the profile identifier
* @param properties
* the encoding properties to be interpreted by the actual encoder implementation
* @return the processed file
* @throws EncoderException
* if processing fails
*/
List<File> process(Map<String, File> source, EncodingProfile profile, Map<String, String> properties) throws EncoderException {
// Fist, update the parameters
Map<String, String> params = new HashMap<>();
if (properties != null)
params.putAll(properties);
// build command
if (source.isEmpty()) {
throw new IllegalArgumentException("At least one track must be specified.");
}
// Set encoding parameters
for (Map.Entry<String, File> f : source.entrySet()) {
final String input = FilenameUtils.normalize(f.getValue().getAbsolutePath());
final String pre = "in." + f.getKey();
params.put(pre + ".path", input);
params.put(pre + ".name", FilenameUtils.getBaseName(input));
params.put(pre + ".suffix", FilenameUtils.getExtension(input));
params.put(pre + ".filename", FilenameUtils.getName(input));
params.put(pre + ".mimetype", MimetypesFileTypeMap.getDefaultFileTypeMap().getContentType(input));
}
final File parentFile = source.getOrDefault("video", source.get("audio"));
final String outDir = parentFile.getAbsoluteFile().getParent();
final String outFileName = FilenameUtils.getBaseName(parentFile.getName()) + "_" + UUID.randomUUID().toString();
params.put("out.dir", outDir);
params.put("out.name", outFileName);
if (profile.getSuffix() != null) {
final String outSuffix = processParameters(profile.getSuffix(), params);
params.put("out.suffix", outSuffix);
}
for (String tag : profile.getTags()) {
final String suffix = processParameters(profile.getSuffix(tag), params);
params.put("out.suffix." + tag, suffix);
}
// create encoder process.
final List<String> command = buildCommand(profile, params);
logger.info("Executing encoding command: {}", command);
List<File> outFiles = new ArrayList<>();
BufferedReader in = null;
Process encoderProcess = null;
try {
ProcessBuilder processBuilder = new ProcessBuilder(command);
processBuilder.redirectErrorStream(REDIRECT_ERROR_STREAM);
encoderProcess = processBuilder.start();
processes.add(encoderProcess);
// tell encoder listeners about output
in = new BufferedReader(new InputStreamReader(encoderProcess.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
handleEncoderOutput(outFiles, line);
}
// wait until the task is finished
int exitCode = encoderProcess.waitFor();
if (exitCode != 0) {
throw new EncoderException("Encoder exited abnormally with status " + exitCode);
}
logger.info("Tracks {} successfully encoded using profile '{}'", source, profile.getIdentifier());
return outFiles;
} catch (Exception e) {
logger.warn("Error while encoding {} using profile '{}'", source, profile.getIdentifier(), e);
// Ensure temporary data are removed
for (File outFile : outFiles) {
if (FileUtils.deleteQuietly(outFile)) {
logger.debug("Removed output file of failed encoding process: {}", outFile);
}
}
throw new EncoderException(e);
} finally {
IoSupport.closeQuietly(in);
IoSupport.closeQuietly(encoderProcess);
}
}
use of org.opencastproject.composer.api.EncoderException in project opencast by opencast.
the class ComposerRestService method image.
/**
* Encodes a track in a media package.
*
* @param sourceTrackXml
* The source track
* @param profileId
* The profile to use in encoding this track
* @param times
* one or more times in seconds separated by comma
* @return A {@link Response} with the resulting track in the response body
* @throws Exception
*/
@POST
@Path("image")
@Produces(MediaType.TEXT_XML)
@RestQuery(name = "image", description = "Starts an image extraction process, based on the specified encoding profile ID and the source track", restParameters = { @RestParameter(description = "The track containing the video stream", isRequired = true, name = "sourceTrack", type = Type.TEXT, defaultValue = "${this.videoTrackDefault}"), @RestParameter(description = "The encoding profile to use", isRequired = true, name = "profileId", type = Type.STRING, defaultValue = "player-preview.http"), @RestParameter(description = "The number of seconds (many numbers can be specified, separated by semicolon) into the video to extract the image", isRequired = false, name = "time", type = Type.STRING), @RestParameter(description = "An optional set of key=value\\n properties", isRequired = false, name = "properties", type = TEXT) }, reponses = { @RestResponse(description = "Results in an xml document containing the image attachment", responseCode = HttpServletResponse.SC_OK), @RestResponse(description = "If required parameters aren't set or if sourceTrack isn't from the type Track", responseCode = HttpServletResponse.SC_BAD_REQUEST) }, returnDescription = "The image extraction job")
public Response image(@FormParam("sourceTrack") String sourceTrackXml, @FormParam("profileId") String profileId, @FormParam("time") String times, @FormParam("properties") LocalHashMap localMap) throws Exception {
// Ensure that the POST parameters are present
if (StringUtils.isBlank(sourceTrackXml) || StringUtils.isBlank(profileId))
return Response.status(Response.Status.BAD_REQUEST).entity("sourceTrack and profileId must not be null").build();
// Deserialize the source track
MediaPackageElement sourceTrack = MediaPackageElementParser.getFromXml(sourceTrackXml);
if (!Track.TYPE.equals(sourceTrack.getElementType()))
return Response.status(Response.Status.BAD_REQUEST).entity("sourceTrack element must be of type track").build();
boolean timeBased = false;
double[] timeArray = null;
if (StringUtils.isNotBlank(times)) {
// parse time codes
try {
timeArray = parseTimeArray(times);
} catch (Exception e) {
return Response.status(Response.Status.BAD_REQUEST).entity("could not parse times: invalid format").build();
}
timeBased = true;
} else if (localMap == null) {
return Response.status(Response.Status.BAD_REQUEST).build();
}
try {
// Asynchronously encode the specified tracks
Job job;
if (timeBased) {
job = composerService.image((Track) sourceTrack, profileId, timeArray);
} else {
job = composerService.image((Track) sourceTrack, profileId, localMap.getMap());
}
return Response.ok().entity(new JaxbJob(job)).build();
} catch (EncoderException e) {
logger.warn("Unable to extract image(s): " + e.getMessage());
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
}
}
use of org.opencastproject.composer.api.EncoderException in project opencast by opencast.
the class ComposerRestService method mux.
/**
* Encodes a track.
*
* @param audioSourceTrackXml
* The audio source track
* @param videoSourceTrackXml
* The video source track
* @param profileId
* The profile to use in encoding this track
* @return A response containing the job for this encoding job in the response body.
* @throws Exception
*/
@POST
@Path("mux")
@Produces(MediaType.TEXT_XML)
@RestQuery(name = "mux", description = "Starts an encoding process, which will mux the two tracks using the given encoding profile", restParameters = { @RestParameter(description = "The track containing the audio stream", isRequired = true, name = "sourceAudioTrack", type = Type.TEXT, defaultValue = "${this.audioTrackDefault}"), @RestParameter(description = "The track containing the video stream", isRequired = true, name = "sourceVideoTrack", type = Type.TEXT, defaultValue = "${this.videoTrackDefault}"), @RestParameter(description = "The encoding profile to use", isRequired = true, name = "profileId", type = Type.STRING, defaultValue = "mp4-medium.http") }, reponses = { @RestResponse(description = "Results in an xml document containing the job for the encoding task", responseCode = HttpServletResponse.SC_OK), @RestResponse(description = "If required parameters aren't set or if the source tracks aren't from the type Track", responseCode = HttpServletResponse.SC_BAD_REQUEST) }, returnDescription = "")
public Response mux(@FormParam("audioSourceTrack") String audioSourceTrackXml, @FormParam("videoSourceTrack") String videoSourceTrackXml, @FormParam("profileId") String profileId) throws Exception {
// Ensure that the POST parameters are present
if (StringUtils.isBlank(audioSourceTrackXml) || StringUtils.isBlank(videoSourceTrackXml) || StringUtils.isBlank(profileId)) {
return Response.status(Response.Status.BAD_REQUEST).entity("audioSourceTrack, videoSourceTrack, and profileId must not be null").build();
}
// Deserialize the audio track
MediaPackageElement audioSourceTrack = MediaPackageElementParser.getFromXml(audioSourceTrackXml);
if (!Track.TYPE.equals(audioSourceTrack.getElementType()))
return Response.status(Response.Status.BAD_REQUEST).entity("audioSourceTrack must be of type track").build();
// Deserialize the video track
MediaPackageElement videoSourceTrack = MediaPackageElementParser.getFromXml(videoSourceTrackXml);
if (!Track.TYPE.equals(videoSourceTrack.getElementType()))
return Response.status(Response.Status.BAD_REQUEST).entity("videoSourceTrack must be of type track").build();
try {
// Asynchronously encode the specified tracks
Job job = composerService.mux((Track) videoSourceTrack, (Track) audioSourceTrack, profileId);
return Response.ok().entity(new JaxbJob(job)).build();
} catch (EncoderException e) {
logger.warn("Unable to mux tracks: " + e.getMessage());
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
}
}
Aggregations