use of org.opencastproject.composer.api.EncoderException in project opencast by opencast.
the class CompositeWorkflowOperationHandler method composite.
private WorkflowOperationResult composite(MediaPackage src, WorkflowOperationInstance operation) throws EncoderException, IOException, NotFoundException, MediaPackageException, WorkflowOperationException {
MediaPackage mediaPackage = (MediaPackage) src.clone();
CompositeSettings compositeSettings;
try {
compositeSettings = new CompositeSettings(mediaPackage, operation);
} catch (IllegalArgumentException e) {
logger.warn("Unable to parse composite settings because {}", ExceptionUtils.getStackTrace(e));
return createResult(mediaPackage, Action.SKIP);
}
Option<Attachment> watermarkAttachment = Option.<Attachment>none();
Collection<Attachment> watermarkElements = compositeSettings.getWatermarkSelector().select(mediaPackage, false);
if (watermarkElements.size() > 1) {
logger.warn("More than one watermark attachment has been found for compositing, skipping compositing!: {}", watermarkElements);
return createResult(mediaPackage, Action.SKIP);
} else if (watermarkElements.size() == 0 && compositeSettings.getSourceUrlWatermark() != null) {
logger.info("No watermark found from flavor and tags, take watermark from URL {}", compositeSettings.getSourceUrlWatermark());
Attachment urlAttachment = new AttachmentImpl();
urlAttachment.setIdentifier(compositeSettings.getWatermarkIdentifier());
if (compositeSettings.getSourceUrlWatermark().startsWith("http")) {
urlAttachment.setURI(UrlSupport.uri(compositeSettings.getSourceUrlWatermark()));
} else {
InputStream in = null;
try {
in = UrlSupport.url(compositeSettings.getSourceUrlWatermark()).openStream();
URI imageUrl = workspace.putInCollection(COLLECTION, compositeSettings.getWatermarkIdentifier() + "." + FilenameUtils.getExtension(compositeSettings.getSourceUrlWatermark()), in);
urlAttachment.setURI(imageUrl);
} catch (Exception e) {
logger.warn("Unable to read watermark source url {}: {}", compositeSettings.getSourceUrlWatermark(), e);
throw new WorkflowOperationException("Unable to read watermark source url " + compositeSettings.getSourceUrlWatermark(), e);
} finally {
IOUtils.closeQuietly(in);
}
}
watermarkAttachment = Option.option(urlAttachment);
} else if (watermarkElements.size() == 0 && compositeSettings.getSourceUrlWatermark() == null) {
logger.info("No watermark to composite");
} else {
for (Attachment a : watermarkElements) watermarkAttachment = Option.option(a);
}
Collection<Track> upperElements = compositeSettings.getUpperTrackSelector().select(mediaPackage, false);
Collection<Track> lowerElements = compositeSettings.getLowerTrackSelector().select(mediaPackage, false);
// There is only a single track to work with.
if ((upperElements.size() == 1 && lowerElements.size() == 0) || (upperElements.size() == 0 && lowerElements.size() == 1)) {
for (Track t : upperElements) compositeSettings.setSingleTrack(t);
for (Track t : lowerElements) compositeSettings.setSingleTrack(t);
return handleSingleTrack(mediaPackage, operation, compositeSettings, watermarkAttachment);
} else {
// Look for upper elements matching the tags and flavor
if (upperElements.size() > 1) {
logger.warn("More than one upper track has been found for compositing, skipping compositing!: {}", upperElements);
return createResult(mediaPackage, Action.SKIP);
} else if (upperElements.size() == 0) {
logger.warn("No upper track has been found for compositing, skipping compositing!");
return createResult(mediaPackage, Action.SKIP);
}
for (Track t : upperElements) {
compositeSettings.setUpperTrack(t);
}
// Look for lower elements matching the tags and flavor
if (lowerElements.size() > 1) {
logger.warn("More than one lower track has been found for compositing, skipping compositing!: {}", lowerElements);
return createResult(mediaPackage, Action.SKIP);
} else if (lowerElements.size() == 0) {
logger.warn("No lower track has been found for compositing, skipping compositing!");
return createResult(mediaPackage, Action.SKIP);
}
for (Track t : lowerElements) {
compositeSettings.setLowerTrack(t);
}
return handleMultipleTracks(mediaPackage, operation, compositeSettings, watermarkAttachment);
}
}
use of org.opencastproject.composer.api.EncoderException in project opencast by opencast.
the class ConcatWorkflowOperationHandler method concat.
private WorkflowOperationResult concat(MediaPackage src, WorkflowOperationInstance operation) throws EncoderException, IOException, NotFoundException, MediaPackageException, WorkflowOperationException {
MediaPackage mediaPackage = (MediaPackage) src.clone();
Map<Integer, Tuple<TrackSelector, Boolean>> trackSelectors = getTrackSelectors(operation);
String outputResolution = StringUtils.trimToNull(operation.getConfiguration(OUTPUT_RESOLUTION));
String outputFrameRate = StringUtils.trimToNull(operation.getConfiguration(OUTPUT_FRAMERATE));
String encodingProfile = StringUtils.trimToNull(operation.getConfiguration(ENCODING_PROFILE));
// Skip the worklow if no source-flavors or tags has been configured
if (trackSelectors.isEmpty()) {
logger.warn("No source-tags or source-flavors has been set.");
return createResult(mediaPackage, Action.SKIP);
}
String targetTagsOption = StringUtils.trimToNull(operation.getConfiguration(TARGET_TAGS));
String targetFlavorOption = StringUtils.trimToNull(operation.getConfiguration(TARGET_FLAVOR));
// Target tags
List<String> targetTags = asList(targetTagsOption);
// Target flavor
if (targetFlavorOption == null)
throw new WorkflowOperationException("Target flavor must be set!");
// Find the encoding profile
if (encodingProfile == null)
throw new WorkflowOperationException("Encoding profile must be set!");
EncodingProfile profile = composerService.getProfile(encodingProfile);
if (profile == null)
throw new WorkflowOperationException("Encoding profile '" + encodingProfile + "' was not found");
// Output resolution
if (outputResolution == null)
throw new WorkflowOperationException("Output resolution must be set!");
Dimension outputDimension = null;
if (outputResolution.startsWith(OUTPUT_PART_PREFIX)) {
if (!trackSelectors.keySet().contains(Integer.parseInt(outputResolution.substring(OUTPUT_PART_PREFIX.length()))))
throw new WorkflowOperationException("Output resolution part not set!");
} else {
try {
String[] outputResolutionArray = StringUtils.split(outputResolution, "x");
if (outputResolutionArray.length != 2) {
throw new WorkflowOperationException("Invalid format of output resolution!");
}
outputDimension = Dimension.dimension(Integer.parseInt(outputResolutionArray[0]), Integer.parseInt(outputResolutionArray[1]));
} catch (WorkflowOperationException e) {
throw e;
} catch (Exception e) {
throw new WorkflowOperationException("Unable to parse output resolution!", e);
}
}
float fps = -1.0f;
if (StringUtils.isNotEmpty(outputFrameRate)) {
if (StringUtils.startsWith(outputFrameRate, OUTPUT_PART_PREFIX)) {
if (!NumberUtils.isNumber(outputFrameRate.substring(OUTPUT_PART_PREFIX.length())) || !trackSelectors.keySet().contains(Integer.parseInt(outputFrameRate.substring(OUTPUT_PART_PREFIX.length())))) {
throw new WorkflowOperationException("Output frame rate part not set or invalid!");
}
} else if (NumberUtils.isNumber(outputFrameRate)) {
fps = NumberUtils.toFloat(outputFrameRate);
} else {
throw new WorkflowOperationException("Unable to parse output frame rate!");
}
}
MediaPackageElementFlavor targetFlavor = null;
try {
targetFlavor = MediaPackageElementFlavor.parseFlavor(targetFlavorOption);
if ("*".equals(targetFlavor.getType()) || "*".equals(targetFlavor.getSubtype()))
throw new WorkflowOperationException("Target flavor must have a type and a subtype, '*' are not allowed!");
} catch (IllegalArgumentException e) {
throw new WorkflowOperationException("Target flavor '" + targetFlavorOption + "' is malformed");
}
List<Track> tracks = new ArrayList<Track>();
for (Entry<Integer, Tuple<TrackSelector, Boolean>> trackSelector : trackSelectors.entrySet()) {
Collection<Track> tracksForSelector = trackSelector.getValue().getA().select(mediaPackage, false);
String currentFlavor = StringUtils.join(trackSelector.getValue().getA().getFlavors());
String currentTag = StringUtils.join(trackSelector.getValue().getA().getTags());
if (tracksForSelector.size() > 1) {
logger.warn("More than one track has been found with flavor '{}' and/or tag '{}' for concat operation, skipping concatenation!", currentFlavor, currentTag);
return createResult(mediaPackage, Action.SKIP);
} else if (tracksForSelector.size() == 0 && trackSelector.getValue().getB()) {
logger.warn("No track has been found with flavor '{}' and/or tag '{}' for concat operation, skipping concatenation!", currentFlavor, currentTag);
return createResult(mediaPackage, Action.SKIP);
} else if (tracksForSelector.size() == 0 && !trackSelector.getValue().getB()) {
logger.info("No track has been found with flavor '{}' and/or tag '{}' for concat operation, skipping track!", currentFlavor, currentTag);
continue;
}
for (Track t : tracksForSelector) {
tracks.add(t);
VideoStream[] videoStreams = TrackSupport.byType(t.getStreams(), VideoStream.class);
if (videoStreams.length == 0) {
logger.info("No video stream available in the track with flavor {}! {}", currentFlavor, t);
return createResult(mediaPackage, Action.SKIP);
}
if (StringUtils.startsWith(outputResolution, OUTPUT_PART_PREFIX) && NumberUtils.isNumber(outputResolution.substring(OUTPUT_PART_PREFIX.length())) && trackSelector.getKey() == Integer.parseInt(outputResolution.substring(OUTPUT_PART_PREFIX.length()))) {
outputDimension = new Dimension(videoStreams[0].getFrameWidth(), videoStreams[0].getFrameHeight());
if (!trackSelector.getValue().getB()) {
logger.warn("Output resolution track {} must be mandatory, skipping concatenation!", outputResolution);
return createResult(mediaPackage, Action.SKIP);
}
}
if (fps <= 0 && StringUtils.startsWith(outputFrameRate, OUTPUT_PART_PREFIX) && NumberUtils.isNumber(outputFrameRate.substring(OUTPUT_PART_PREFIX.length())) && trackSelector.getKey() == Integer.parseInt(outputFrameRate.substring(OUTPUT_PART_PREFIX.length()))) {
fps = videoStreams[0].getFrameRate();
}
}
}
if (tracks.size() == 0) {
logger.warn("No tracks found for concating operation, skipping concatenation!");
return createResult(mediaPackage, Action.SKIP);
} else if (tracks.size() == 1) {
Track track = (Track) tracks.get(0).clone();
track.setIdentifier(null);
addNewTrack(mediaPackage, track, targetTags, targetFlavor);
logger.info("At least two tracks are needed for the concating operation, skipping concatenation!");
return createResult(mediaPackage, Action.SKIP);
}
Job concatJob;
if (fps > 0) {
concatJob = composerService.concat(profile.getIdentifier(), outputDimension, fps, tracks.toArray(new Track[tracks.size()]));
} else {
concatJob = composerService.concat(profile.getIdentifier(), outputDimension, tracks.toArray(new Track[tracks.size()]));
}
// Wait for the jobs to return
if (!waitForStatus(concatJob).isSuccess())
throw new WorkflowOperationException("The concat job did not complete successfully");
if (concatJob.getPayload().length() > 0) {
Track concatTrack = (Track) MediaPackageElementParser.getFromXml(concatJob.getPayload());
concatTrack.setURI(workspace.moveTo(concatTrack.getURI(), mediaPackage.getIdentifier().toString(), concatTrack.getIdentifier(), "concat." + FilenameUtils.getExtension(concatTrack.getURI().toString())));
addNewTrack(mediaPackage, concatTrack, targetTags, targetFlavor);
WorkflowOperationResult result = createResult(mediaPackage, Action.CONTINUE, concatJob.getQueueTime());
logger.debug("Concat operation completed");
return result;
} else {
logger.info("concat operation unsuccessful, no payload returned: {}", concatJob);
return createResult(mediaPackage, Action.SKIP);
}
}
use of org.opencastproject.composer.api.EncoderException in project opencast by opencast.
the class ComposerServiceImpl method parallelEncode.
/**
* {@inheritDoc}
*
* @see org.opencastproject.composer.api.ComposerService#encode(org.opencastproject.mediapackage.Track,
* java.lang.String)
*/
@Override
public Job parallelEncode(Track sourceTrack, String profileId) throws EncoderException, MediaPackageException {
try {
final EncodingProfile profile = profileScanner.getProfile(profileId);
logger.info("Starting parallel encode with profile {} with job load {}", profileId, df.format(profile.getJobLoad()));
return serviceRegistry.createJob(JOB_TYPE, Operation.ParallelEncode.toString(), Arrays.asList(profileId, MediaPackageElementParser.getAsXml(sourceTrack)), profile.getJobLoad());
} catch (ServiceRegistryException e) {
throw new EncoderException("Unable to create a job", e);
}
}
use of org.opencastproject.composer.api.EncoderException in project opencast by opencast.
the class ComposerServiceImpl method parallelEncode.
/**
* Encodes audio and video track to a file. If both an audio and a video track are given, they are muxed together into
* one movie container.
*
* @param job
* Job in which context the encoding is done
* @param mediaTrack
* Source track
* @param profileId
* the encoding profile
* @return the encoded track or none if the operation does not return a track. This may happen for example when doing
* two pass encodings where the first pass only creates metadata for the second one
* @throws EncoderException
* if encoding fails
*/
private List<Track> parallelEncode(Job job, Track mediaTrack, String profileId) throws EncoderException, MediaPackageException {
if (job == null) {
throw new EncoderException("The Job parameter must not be null");
}
// Get the tracks and make sure they exist
final File mediaFile = loadTrackIntoWorkspace(job, "source", mediaTrack);
// Create the engine
final EncodingProfile profile = getProfile(profileId);
final EncoderEngine encoderEngine = getEncoderEngine();
// List of encoded tracks
LinkedList<Track> encodedTracks = new LinkedList<>();
// Do the work
int i = 0;
Map<String, File> source = new HashMap<>();
source.put("video", mediaFile);
List<File> outputFiles = encoderEngine.process(source, profile, null);
activeEncoder.remove(encoderEngine);
for (File encodingOutput : outputFiles) {
// Put the file in the workspace
URI returnURL;
final String targetTrackId = idBuilder.createNew().toString();
try (InputStream in = new FileInputStream(encodingOutput)) {
returnURL = workspace.putInCollection(COLLECTION, job.getId() + "-" + i + "." + FilenameUtils.getExtension(encodingOutput.getAbsolutePath()), in);
logger.info("Copied the encoded file to the workspace at {}", returnURL);
if (encodingOutput.delete()) {
logger.info("Deleted the local copy of the encoded file at {}", encodingOutput.getAbsolutePath());
} else {
logger.warn("Unable to delete the encoding output at {}", encodingOutput);
}
} catch (Exception e) {
throw new EncoderException("Unable to put the encoded file into the workspace", e);
}
// Have the encoded track inspected and return the result
Job inspectionJob = inspect(job, returnURL);
Track inspectedTrack = (Track) MediaPackageElementParser.getFromXml(inspectionJob.getPayload());
inspectedTrack.setIdentifier(targetTrackId);
List<String> tags = profile.getTags();
for (String tag : tags) {
if (encodingOutput.getName().endsWith(profile.getSuffix(tag)))
inspectedTrack.addTag(tag);
}
encodedTracks.add(inspectedTrack);
i++;
}
return encodedTracks;
}
use of org.opencastproject.composer.api.EncoderException in project opencast by opencast.
the class ComposerServiceImpl method trim.
/**
* Trims the given track using the encoding profile <code>profileId</code> and the given starting point and duration
* in miliseconds.
*
* @param job
* the associated job
* @param sourceTrack
* the source track
* @param profileId
* the encoding profile identifier
* @param start
* the trimming in-point in millis
* @param duration
* the trimming duration in millis
* @return the trimmed track or none if the operation does not return a track. This may happen for example when doing
* two pass encodings where the first pass only creates metadata for the second one
* @throws EncoderException
* if trimming fails
*/
private Option<Track> trim(Job job, Track sourceTrack, String profileId, long start, long duration) throws EncoderException {
String targetTrackId = idBuilder.createNew().toString();
// Get the track and make sure it exists
final File trackFile = loadTrackIntoWorkspace(job, "source", sourceTrack);
// Get the encoding profile
final EncodingProfile profile = getProfile(job, profileId);
// Create the engine
final EncoderEngine encoderEngine = getEncoderEngine();
File output;
try {
output = encoderEngine.trim(trackFile, profile, start, duration, null);
} catch (EncoderException e) {
Map<String, String> params = new HashMap<>();
params.put("track", sourceTrack.getURI().toString());
params.put("profile", profile.getIdentifier());
params.put("start", Long.toString(start));
params.put("duration", Long.toString(duration));
incident().recordFailure(job, TRIMMING_FAILED, e, params, detailsFor(e, encoderEngine));
throw e;
} finally {
activeEncoder.remove(encoderEngine);
}
// trim did not return a file
if (!output.exists() || output.length() == 0)
return none();
// Put the file in the workspace
URI workspaceURI = putToCollection(job, output, "trimmed file");
// Have the encoded track inspected and return the result
Job inspectionJob = inspect(job, workspaceURI);
try {
Track inspectedTrack = (Track) MediaPackageElementParser.getFromXml(inspectionJob.getPayload());
inspectedTrack.setIdentifier(targetTrackId);
return some(inspectedTrack);
} catch (MediaPackageException e) {
throw new EncoderException(e);
}
}
Aggregations