use of org.opencastproject.mediapackage.Attachment in project opencast by opencast.
the class ComposerServiceImpl method buildCompositeCommand.
/**
* Example composite command below. Use with `-filter_complex` option of ffmpeg if upper video is available otherwise
* use -filver:v option for a single video.
*
* Dual video sample: The ffmpeg command needs two source files set with the `-i` option. The first media file is the
* `lower`, the second the `upper` one. Example filter: -filter_complex
* [0:v]scale=909:682,pad=1280:720:367:4:0x444345FF[lower];[1:v]scale=358:151[upper];[lower][upper]overlay=4:4[out]
*
* Single video sample: The ffmpeg command needs one source files set with the `-i` option. Example filter: filter:v
* [in]scale=909:682,pad=1280:720:367:4:0x444345FF[out]
*
* @return commandline part with -filter_complex and -map options
*/
private static String buildCompositeCommand(Dimension compositeTrackSize, LaidOutElement<Track> lowerLaidOutElement, Option<LaidOutElement<Track>> upperLaidOutElement, Option<File> upperFile, Option<LaidOutElement<Attachment>> watermarkOption, File watermarkFile, String backgroundColor) {
final StringBuilder cmd = new StringBuilder();
final String videoId = watermarkOption.isNone() ? "[out]" : "[video]";
if (upperLaidOutElement.isNone()) {
// There is only one video track and possibly one watermark.
final Layout videoLayout = lowerLaidOutElement.getLayout();
final String videoPosition = videoLayout.getOffset().getX() + ":" + videoLayout.getOffset().getY();
final String scaleVideo = videoLayout.getDimension().getWidth() + ":" + videoLayout.getDimension().getHeight();
final String padLower = compositeTrackSize.getWidth() + ":" + compositeTrackSize.getHeight() + ":" + videoPosition + ":" + backgroundColor;
cmd.append("-filter:v [in]scale=").append(scaleVideo).append(",pad=").append(padLower).append(videoId);
} else if (upperFile.isSome() && upperLaidOutElement.isSome()) {
// There are two video tracks to handle.
final Layout lowerLayout = lowerLaidOutElement.getLayout();
final Layout upperLayout = upperLaidOutElement.get().getLayout();
final String upperPosition = upperLayout.getOffset().getX() + ":" + upperLayout.getOffset().getY();
final String lowerPosition = lowerLayout.getOffset().getX() + ":" + lowerLayout.getOffset().getY();
final String scaleUpper = upperLayout.getDimension().getWidth() + ":" + upperLayout.getDimension().getHeight();
final String scaleLower = lowerLayout.getDimension().getWidth() + ":" + lowerLayout.getDimension().getHeight();
final String padLower = compositeTrackSize.getWidth() + ":" + compositeTrackSize.getHeight() + ":" + lowerPosition + ":" + backgroundColor;
// Add input file for the upper track
cmd.append("-i ").append(upperFile.get().getAbsolutePath()).append(" ");
// Add filter complex mode
cmd.append("-filter_complex").append(" [0:v]scale=").append(scaleLower).append(",pad=").append(padLower).append("[lower]").append(";[1:v]scale=").append(scaleUpper).append("[upper]").append(";[lower][upper]overlay=").append(upperPosition).append(videoId);
}
for (final LaidOutElement<Attachment> watermarkLayout : watermarkOption) {
String watermarkPosition = watermarkLayout.getLayout().getOffset().getX() + ":" + watermarkLayout.getLayout().getOffset().getY();
cmd.append(";").append("movie=").append(watermarkFile.getAbsoluteFile()).append("[watermark];").append(videoId).append("[watermark]overlay=").append(watermarkPosition).append("[out]");
}
if (upperLaidOutElement.isSome()) {
// handle audio
// if both videos contain audio mix it into a single audio stream
final boolean lowerAudio = lowerLaidOutElement.getElement().hasAudio();
final boolean upperAudio = upperLaidOutElement.get().getElement().hasAudio();
if (lowerAudio && upperAudio) {
cmd.append(";[0:a][1:a]amix=inputs=2[aout] -map [out] -map [aout]");
} else if (lowerAudio) {
cmd.append(" -map [out] -map 0:a");
} else if (upperAudio) {
cmd.append(" -map [out] -map 1:a");
} else {
cmd.append(" -map [out]");
}
}
return cmd.toString();
}
use of org.opencastproject.mediapackage.Attachment in project opencast by opencast.
the class AbstractCoverImageService method process.
@Override
protected String process(Job job) throws Exception {
List<String> arguments = job.getArguments();
String xml = arguments.get(0);
String xsl = arguments.get(1);
int width = Integer.valueOf(arguments.get(2));
int height = Integer.valueOf(arguments.get(3));
String posterImage = arguments.get(4);
String targetFlavor = arguments.get(5);
Operation op = null;
op = Operation.valueOf(job.getOperation());
switch(op) {
case Generate:
Attachment result = generateCoverImageInternal(job, xml, xsl, width, height, posterImage, targetFlavor);
return MediaPackageElementParser.getAsXml(result);
default:
throw new IllegalStateException("Don't know how to handle operation '" + job.getOperation() + "'");
}
}
use of org.opencastproject.mediapackage.Attachment in project opencast by opencast.
the class CoverImageWorkflowOperationHandlerBase method start.
@Override
public WorkflowOperationResult start(final WorkflowInstance workflowInstance, JobContext context) throws WorkflowOperationException {
MediaPackage mediaPackage = workflowInstance.getMediaPackage();
WorkflowOperationInstance operation = workflowInstance.getCurrentOperation();
logger.info("Cover Image Workflow started for media package '{}'", mediaPackage.getIdentifier());
// User XML metadata from operation configuration, fallback to default metadata
String xml = operation.getConfiguration(XML_METADATA);
if (xml == null) {
xml = getMetadataXml(mediaPackage);
logger.debug("Metadata was not part of operation configuration, using Dublin Core as fallback");
}
logger.debug("Metadata set to: {}", xml);
String xsl = loadXsl(operation);
logger.debug("XSL for transforming metadata to SVG loaded: {}", xsl);
// Read image dimensions
int width = getIntConfiguration(operation, WIDTH);
logger.debug("Image width set to {}px", width);
int height = getIntConfiguration(operation, HEIGHT);
logger.debug("Image height set to {}px", height);
// Read optional poster image flavor
String posterImgUri = getPosterImageFileUrl(operation.getConfiguration(POSTERIMAGE_URL));
if (posterImgUri == null)
posterImgUri = getPosterImageFileUrl(mediaPackage, operation.getConfiguration(POSTERIMAGE_FLAVOR));
if (posterImgUri == null) {
logger.debug("No optional poster image set");
} else {
logger.debug("Poster image found at '{}'", posterImgUri);
}
// Read target flavor
String targetFlavor = operation.getConfiguration(TARGET_FLAVOR);
if (StringUtils.isBlank(targetFlavor)) {
logger.warn("Required configuration key '{}' is blank", TARGET_FLAVOR);
throw new WorkflowOperationException("Configuration key '" + TARGET_FLAVOR + "' must be set");
}
try {
MediaPackageElementFlavor.parseFlavor(targetFlavor);
} catch (IllegalArgumentException e) {
logger.warn("Given target flavor '{}' is not a valid flavor", targetFlavor);
throw new WorkflowOperationException(e);
}
Job generate;
try {
generate = getCoverImageService().generateCoverImage(xml, xsl, String.valueOf(width), String.valueOf(height), posterImgUri, targetFlavor);
logger.debug("Job for cover image generation created");
if (!waitForStatus(generate).isSuccess()) {
throw new WorkflowOperationException("'Cover image' job did not successfuly end");
}
generate = serviceRegistry.getJob(generate.getId());
Attachment coverImage = (Attachment) MediaPackageElementParser.getFromXml(generate.getPayload());
URI attachmentUri = getWorkspace().moveTo(coverImage.getURI(), mediaPackage.getIdentifier().compact(), UUID.randomUUID().toString(), COVERIMAGE_FILENAME);
coverImage.setURI(attachmentUri);
coverImage.setMimeType(MimeTypes.PNG);
// Add tags
final String targetTags = StringUtils.trimToNull(operation.getConfiguration(TARGET_TAGS));
if (targetTags != null) {
for (String tag : asList(targetTags)) {
logger.trace("Tagging image with '{}'", tag);
if (StringUtils.trimToNull(tag) != null)
coverImage.addTag(tag);
}
}
mediaPackage.add(coverImage);
} catch (MediaPackageException e) {
throw new WorkflowOperationException(e);
} catch (NotFoundException e) {
throw new WorkflowOperationException(e);
} catch (ServiceRegistryException e) {
throw new WorkflowOperationException(e);
} catch (CoverImageException e) {
throw new WorkflowOperationException(e);
} catch (IllegalArgumentException e) {
throw new WorkflowOperationException(e);
} catch (IOException e) {
throw new WorkflowOperationException(e);
}
logger.info("Cover Image Workflow finished successfully for media package '{}' within {}ms", mediaPackage.getIdentifier(), generate.getQueueTime());
return createResult(mediaPackage, Action.CONTINUE, generate.getQueueTime());
}
use of org.opencastproject.mediapackage.Attachment 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.mediapackage.Attachment in project opencast by opencast.
the class SeriesUpdatedEventHandler method handleEvent.
public void handleEvent(final SeriesItem seriesItem) {
// A series or its ACL has been updated. Find any mediapackages with that series, and update them.
logger.debug("Handling {}", seriesItem);
String seriesId = seriesItem.getSeriesId();
// We must be an administrative user to make this query
final User prevUser = securityService.getUser();
final Organization prevOrg = securityService.getOrganization();
try {
securityService.setUser(SecurityUtil.createSystemUser(systemAccount, prevOrg));
SearchQuery q = new SearchQuery().withSeriesId(seriesId);
SearchResult result = searchService.getForAdministrativeRead(q);
for (SearchResultItem item : result.getItems()) {
MediaPackage mp = item.getMediaPackage();
Organization org = organizationDirectoryService.getOrganization(item.getOrganization());
securityService.setOrganization(org);
// to the distribution channels as well
if (SeriesItem.Type.UpdateAcl.equals(seriesItem.getType())) {
// Build a new XACML file for this mediapackage
Attachment fileRepoCopy = authorizationService.setAcl(mp, AclScope.Series, seriesItem.getAcl()).getB();
// Distribute the updated XACML file
Job distributionJob = distributionService.distribute(CHANNEL_ID, mp, fileRepoCopy.getIdentifier());
JobBarrier barrier = new JobBarrier(null, serviceRegistry, distributionJob);
Result jobResult = barrier.waitForJobs();
if (jobResult.getStatus().get(distributionJob).equals(FINISHED)) {
mp.remove(fileRepoCopy);
mp.add(getFromXml(serviceRegistry.getJob(distributionJob.getId()).getPayload()));
} else {
logger.error("Unable to distribute XACML {}", fileRepoCopy.getIdentifier());
continue;
}
}
// Update the series dublin core
if (SeriesItem.Type.UpdateCatalog.equals(seriesItem.getType())) {
DublinCoreCatalog seriesDublinCore = seriesItem.getMetadata();
mp.setSeriesTitle(seriesDublinCore.getFirst(DublinCore.PROPERTY_TITLE));
// Update the series dublin core
Catalog[] seriesCatalogs = mp.getCatalogs(MediaPackageElements.SERIES);
if (seriesCatalogs.length == 1) {
Catalog c = seriesCatalogs[0];
String filename = FilenameUtils.getName(c.getURI().toString());
URI uri = workspace.put(mp.getIdentifier().toString(), c.getIdentifier(), filename, dublinCoreService.serialize(seriesDublinCore));
c.setURI(uri);
// setting the URI to a new source so the checksum will most like be invalid
c.setChecksum(null);
// Distribute the updated series dc
Job distributionJob = distributionService.distribute(CHANNEL_ID, mp, c.getIdentifier());
JobBarrier barrier = new JobBarrier(null, serviceRegistry, distributionJob);
Result jobResult = barrier.waitForJobs();
if (jobResult.getStatus().get(distributionJob).equals(FINISHED)) {
mp.remove(c);
mp.add(getFromXml(serviceRegistry.getJob(distributionJob.getId()).getPayload()));
} else {
logger.error("Unable to distribute series catalog {}", c.getIdentifier());
continue;
}
}
}
// Remove the series catalog and isPartOf from episode catalog
if (SeriesItem.Type.Delete.equals(seriesItem.getType())) {
mp.setSeries(null);
mp.setSeriesTitle(null);
boolean retractSeriesCatalog = retractSeriesCatalog(mp);
boolean updateEpisodeCatalog = updateEpisodeCatalog(mp);
if (!retractSeriesCatalog || !updateEpisodeCatalog)
continue;
}
// Update the search index with the modified mediapackage
Job searchJob = searchService.add(mp);
JobBarrier barrier = new JobBarrier(null, serviceRegistry, searchJob);
barrier.waitForJobs();
}
} catch (SearchException e) {
logger.warn("Unable to find mediapackages in search: ", e.getMessage());
} catch (UnauthorizedException e) {
logger.warn(e.getMessage());
} catch (MediaPackageException e) {
logger.warn(e.getMessage());
} catch (ServiceRegistryException e) {
logger.warn(e.getMessage());
} catch (NotFoundException e) {
logger.warn(e.getMessage());
} catch (IOException e) {
logger.warn(e.getMessage());
} catch (DistributionException e) {
logger.warn(e.getMessage());
} finally {
securityService.setOrganization(prevOrg);
securityService.setUser(prevUser);
}
}
Aggregations