use of org.opencastproject.mediapackage.Attachment in project opencast by opencast.
the class PartialImportWorkflowOperationHandler method processChildren.
private long processChildren(long position, List<Track> tracks, NodeList children, List<Track> originalTracks, VCell<String> type, String mediaType, List<MediaPackageElement> elementsToClean, Long operationId) throws EncoderException, MediaPackageException, WorkflowOperationException, NotFoundException, IOException {
for (int j = 0; j < children.getLength(); j++) {
Node item = children.item(j);
if (item.hasChildNodes()) {
position = processChildren(position, tracks, item.getChildNodes(), originalTracks, type, mediaType, elementsToClean, operationId);
} else {
SMILMediaElement e = (SMILMediaElement) item;
if (mediaType.equals(e.getNodeName())) {
Track track = getFromOriginal(e.getId(), originalTracks, type);
double beginInSeconds = e.getBegin().item(0).getResolvedOffset();
long beginInMs = Math.round(beginInSeconds * 1000d);
// Fill out gaps with first or last frame from video
if (beginInMs > position) {
double positionInSeconds = position / 1000d;
if (position == 0) {
if (NODE_TYPE_AUDIO.equals(e.getNodeName())) {
logger.info("Extending {} audio track start by {} seconds silent audio", type.get(), beginInSeconds);
tracks.add(getSilentAudio(beginInSeconds, elementsToClean, operationId));
} else {
logger.info("Extending {} track start image frame by {} seconds", type.get(), beginInSeconds);
Attachment tempFirstImageFrame = extractImage(track, 0, elementsToClean);
tracks.add(createVideoFromImage(tempFirstImageFrame, beginInSeconds, elementsToClean));
}
position += beginInMs;
} else {
double fillTime = (beginInMs - position) / 1000d;
if (NODE_TYPE_AUDIO.equals(e.getNodeName())) {
logger.info("Fill {} audio track gap from {} to {} with silent audio", type.get(), Double.toString(positionInSeconds), Double.toString(beginInSeconds));
tracks.add(getSilentAudio(fillTime, elementsToClean, operationId));
} else {
logger.info("Fill {} track gap from {} to {} with image frame", type.get(), Double.toString(positionInSeconds), Double.toString(beginInSeconds));
Track previousTrack = tracks.get(tracks.size() - 1);
Attachment tempLastImageFrame = extractLastImageFrame(previousTrack, elementsToClean);
tracks.add(createVideoFromImage(tempLastImageFrame, fillTime, elementsToClean));
}
position = beginInMs;
}
}
tracks.add(track);
position += Math.round(e.getDur() * 1000f);
}
}
}
return position;
}
use of org.opencastproject.mediapackage.Attachment in project opencast by opencast.
the class AbstractCoverImageService method generateCoverImageInternal.
protected Attachment generateCoverImageInternal(Job job, String xml, String xsl, int width, int height, String posterImage, String targetFlavor) throws CoverImageException {
URI result;
File tempSvg = null;
File tempPng = null;
StringReader xmlReader = null;
try {
Document xslDoc = parseXsl(xsl);
// Create temp SVG file for transformation result
tempSvg = createTempFile(job, ".svg");
Result svg = new StreamResult(tempSvg);
// Load Metadata (from resources)
xmlReader = new StringReader(xml);
Source xmlSource = new StreamSource(xmlReader);
// Transform XML metadata with stylesheet to SVG
transformSvg(svg, xmlSource, xslDoc, width, height, posterImage);
// Rasterize SVG to PNG
tempPng = createTempFile(job, ".png");
rasterizeSvg(tempSvg, tempPng);
FileInputStream in = null;
try {
in = new FileInputStream(tempPng);
result = workspace.putInCollection(COVERIMAGE_WORKSPACE_COLLECTION, job.getId() + "_coverimage.png", in);
log.debug("Put the cover image into the workspace ({})", result);
} catch (FileNotFoundException e) {
// should never happen...
throw new CoverImageException(e);
} catch (IOException e) {
log.warn("Error while putting resulting image into workspace collection '{}': {}", COVERIMAGE_WORKSPACE_COLLECTION, e);
throw new CoverImageException("Error while putting resulting image into workspace collection", e);
} finally {
IOUtils.closeQuietly(in);
}
} finally {
FileUtils.deleteQuietly(tempSvg);
FileUtils.deleteQuietly(tempPng);
log.debug("Removed temporary files");
IOUtils.closeQuietly(xmlReader);
}
return (Attachment) MediaPackageElementBuilderFactory.newInstance().newElementBuilder().elementFromURI(result, Type.Attachment, MediaPackageElementFlavor.parseFlavor(targetFlavor));
}
use of org.opencastproject.mediapackage.Attachment in project opencast by opencast.
the class CoverImageWorkflowOperationHandlerBase method getPosterImageFileUrl.
protected String getPosterImageFileUrl(MediaPackage mediaPackage, String posterimageFlavor) throws WorkflowOperationException {
if (posterimageFlavor == null) {
logger.debug("Optional configuration key '{}' not set", POSTERIMAGE_FLAVOR);
return null;
}
MediaPackageElementFlavor flavor;
try {
flavor = MediaPackageElementFlavor.parseFlavor(posterimageFlavor);
} catch (IllegalArgumentException e) {
logger.warn("'{}' is not a valid flavor", posterimageFlavor);
throw new WorkflowOperationException(e);
}
Attachment[] atts = mediaPackage.getAttachments(flavor);
if (atts.length > 1) {
logger.warn("More than one attachment with the flavor '{}' found in media package '{}'", posterimageFlavor, mediaPackage.getIdentifier());
throw new WorkflowOperationException("More than one attachment with the flavor'" + posterimageFlavor + "' found.");
} else if (atts.length == 0) {
logger.warn("No attachment with the flavor '{}' found in media package '{}'", posterimageFlavor, mediaPackage.getIdentifier());
return null;
}
try {
return getWorkspace().get(atts[0].getURI()).getAbsolutePath();
} catch (NotFoundException e) {
throw new WorkflowOperationException(e);
} catch (IOException e) {
throw new WorkflowOperationException(e);
}
}
use of org.opencastproject.mediapackage.Attachment in project opencast by opencast.
the class TextAnalysisRestEndpoint method analyze.
@POST
@Produces(MediaType.TEXT_XML)
@Path("")
@RestQuery(name = "analyze", description = "Submit a track for analysis.", restParameters = { @RestParameter(description = "The image to analyze for text.", isRequired = true, name = "image", type = RestParameter.Type.FILE) }, reponses = { @RestResponse(description = "OK, The receipt to use when polling for the resulting mpeg7 catalog.", responseCode = HttpServletResponse.SC_OK), @RestResponse(description = "The argument cannot be parsed into a media package element.", responseCode = HttpServletResponse.SC_BAD_REQUEST), @RestResponse(description = "The service is unavailable at the moment.", responseCode = HttpServletResponse.SC_SERVICE_UNAVAILABLE) }, returnDescription = "The receipt to use when polling for the resulting mpeg7 catalog.")
public Response analyze(@FormParam("image") String image) {
if (service == null)
throw new WebApplicationException(Status.SERVICE_UNAVAILABLE);
try {
MediaPackageElement element = MediaPackageElementParser.getFromXml(image);
if (!(element instanceof Attachment))
throw new WebApplicationException(Status.BAD_REQUEST);
Job job = service.extract((Attachment) element);
return Response.ok(new JaxbJob(job)).build();
} catch (Exception e) {
logger.info(e.getMessage(), e);
return Response.serverError().build();
}
}
use of org.opencastproject.mediapackage.Attachment in project opencast by opencast.
the class TextAnalysisWorkflowOperationHandler method extractVideoText.
/**
* Runs the text analysis service on each of the video segments found.
*
* @param mediaPackage
* the original mediapackage
* @param operation
* the workflow operation
* @throws ExecutionException
* @throws InterruptedException
* @throws NotFoundException
* @throws WorkflowOperationException
*/
protected WorkflowOperationResult extractVideoText(final MediaPackage mediaPackage, WorkflowOperationInstance operation) throws EncoderException, InterruptedException, ExecutionException, IOException, NotFoundException, MediaPackageException, TextAnalyzerException, WorkflowOperationException, ServiceRegistryException {
long totalTimeInQueue = 0;
List<String> sourceTagSet = asList(operation.getConfiguration("source-tags"));
List<String> targetTagSet = asList(operation.getConfiguration("target-tags"));
// Select the catalogs according to the tags
Map<Catalog, Mpeg7Catalog> catalogs = loadSegmentCatalogs(mediaPackage, operation);
// Was there at least one matching catalog
if (catalogs.size() == 0) {
logger.debug("Mediapackage {} has no suitable mpeg-7 catalogs based on tags {} to to run text analysis", mediaPackage, sourceTagSet);
return createResult(mediaPackage, Action.CONTINUE);
}
// Loop over all existing segment catalogs
for (Entry<Catalog, Mpeg7Catalog> mapEntry : catalogs.entrySet()) {
Map<VideoSegment, Job> jobs = new HashMap<VideoSegment, Job>();
List<Attachment> images = new LinkedList<Attachment>();
Catalog segmentCatalog = mapEntry.getKey();
try {
MediaPackageReference catalogRef = segmentCatalog.getReference();
// Make sure we can figure out the source track
if (catalogRef == null) {
logger.info("Skipping catalog {} since we can't determine the source track", segmentCatalog);
} else if (mediaPackage.getElementByReference(catalogRef) == null) {
logger.info("Skipping catalog {} since we can't determine the source track", segmentCatalog);
} else if (!(mediaPackage.getElementByReference(catalogRef) instanceof Track)) {
logger.info("Skipping catalog {} since it's source was not a track", segmentCatalog);
}
logger.info("Analyzing mpeg-7 segments catalog {} for text", segmentCatalog);
// Create a copy that will contain the segments enriched with the video text elements
Mpeg7Catalog textCatalog = mapEntry.getValue().clone();
Track sourceTrack = mediaPackage.getTrack(catalogRef.getIdentifier());
// Load the temporal decomposition (segments)
Video videoContent = textCatalog.videoContent().next();
TemporalDecomposition<? extends Segment> decomposition = videoContent.getTemporalDecomposition();
Iterator<? extends Segment> segmentIterator = decomposition.segments();
// For every segment, try to find the still image and run text analysis on it
List<VideoSegment> videoSegments = new LinkedList<VideoSegment>();
while (segmentIterator.hasNext()) {
Segment segment = segmentIterator.next();
if ((segment instanceof VideoSegment))
videoSegments.add((VideoSegment) segment);
}
// argument array for image extraction
long[] times = new long[videoSegments.size()];
for (int i = 0; i < videoSegments.size(); i++) {
VideoSegment videoSegment = videoSegments.get(i);
MediaTimePoint segmentTimePoint = videoSegment.getMediaTime().getMediaTimePoint();
MediaDuration segmentDuration = videoSegment.getMediaTime().getMediaDuration();
// Choose a time
MediaPackageReference reference = null;
if (catalogRef == null)
reference = new MediaPackageReferenceImpl();
else
reference = new MediaPackageReferenceImpl(catalogRef.getType(), catalogRef.getIdentifier());
reference.setProperty("time", segmentTimePoint.toString());
// Have the time for ocr image created. To circumvent problems with slowly building slides, we take the image
// that is
// almost at the end of the segment, it should contain the most content and is stable as well.
long startTimeSeconds = segmentTimePoint.getTimeInMilliseconds() / 1000;
long durationSeconds = segmentDuration.getDurationInMilliseconds() / 1000;
times[i] = Math.max(startTimeSeconds + durationSeconds - stabilityThreshold + 1, 0);
}
// Have the ocr image(s) created.
// TODO: Note that the way of having one image extracted after the other is suited for
// the ffmpeg-based encoder. When switching to other encoding engines such as gstreamer, it might be preferable
// to pass in all timepoints to the image extraction method at once.
SortedMap<Long, Job> extractImageJobs = new TreeMap<Long, Job>();
try {
for (long time : times) {
extractImageJobs.put(time, composer.image(sourceTrack, IMAGE_EXTRACTION_PROFILE, time));
}
if (!waitForStatus(extractImageJobs.values().toArray(new Job[extractImageJobs.size()])).isSuccess())
throw new WorkflowOperationException("Extracting scene image from " + sourceTrack + " failed");
for (Map.Entry<Long, Job> entry : extractImageJobs.entrySet()) {
Job job = serviceRegistry.getJob(entry.getValue().getId());
Attachment image = (Attachment) MediaPackageElementParser.getFromXml(job.getPayload());
images.add(image);
totalTimeInQueue += job.getQueueTime();
}
} catch (EncoderException e) {
logger.error("Error creating still image(s) from {}", sourceTrack);
throw e;
}
// Run text extraction on each of the images
Iterator<VideoSegment> it = videoSegments.iterator();
for (MediaPackageElement element : images) {
Attachment image = (Attachment) element;
VideoSegment videoSegment = it.next();
jobs.put(videoSegment, analysisService.extract(image));
}
// Wait for all jobs to be finished
if (!waitForStatus(jobs.values().toArray(new Job[jobs.size()])).isSuccess()) {
throw new WorkflowOperationException("Text extraction failed on images from " + sourceTrack);
}
// Process the text extraction results
for (Map.Entry<VideoSegment, Job> entry : jobs.entrySet()) {
Job job = serviceRegistry.getJob(entry.getValue().getId());
totalTimeInQueue += job.getQueueTime();
VideoSegment videoSegment = entry.getKey();
MediaDuration segmentDuration = videoSegment.getMediaTime().getMediaDuration();
Catalog catalog = (Catalog) MediaPackageElementParser.getFromXml(job.getPayload());
if (catalog == null) {
logger.warn("Text analysis did not return a valid mpeg7 for segment {}", videoSegment);
continue;
}
Mpeg7Catalog videoTextCatalog = loadMpeg7Catalog(catalog);
if (videoTextCatalog == null)
throw new IllegalStateException("Text analysis service did not return a valid mpeg7");
// Add the spatiotemporal decompositions from the new catalog to the existing video segments
Iterator<Video> videoTextContents = videoTextCatalog.videoContent();
if (videoTextContents == null || !videoTextContents.hasNext()) {
logger.debug("Text analysis was not able to extract any text from {}", job.getArguments().get(0));
break;
}
try {
Video textVideoContent = videoTextContents.next();
VideoSegment textVideoSegment = (VideoSegment) textVideoContent.getTemporalDecomposition().segments().next();
VideoText[] videoTexts = textVideoSegment.getSpatioTemporalDecomposition().getVideoText();
SpatioTemporalDecomposition std = videoSegment.createSpatioTemporalDecomposition(true, false);
for (VideoText videoText : videoTexts) {
MediaTime mediaTime = new MediaTimeImpl(new MediaRelTimePointImpl(0), segmentDuration);
SpatioTemporalLocator locator = new SpatioTemporalLocatorImpl(mediaTime);
videoText.setSpatioTemporalLocator(locator);
std.addVideoText(videoText);
}
} catch (Exception e) {
logger.warn("The mpeg-7 structure returned by the text analyzer is not what is expected", e);
continue;
}
}
// Put the catalog into the workspace and add it to the media package
MediaPackageElementBuilder builder = MediaPackageElementBuilderFactory.newInstance().newElementBuilder();
Catalog catalog = (Catalog) builder.newElement(MediaPackageElement.Type.Catalog, MediaPackageElements.TEXTS);
catalog.setIdentifier(null);
catalog.setReference(segmentCatalog.getReference());
// the catalog now has an ID, so we can store the file properly
mediaPackage.add(catalog);
InputStream in = mpeg7CatalogService.serialize(textCatalog);
String filename = "slidetext.xml";
URI workspaceURI = workspace.put(mediaPackage.getIdentifier().toString(), catalog.getIdentifier(), filename, in);
catalog.setURI(workspaceURI);
// Since we've enriched and stored the mpeg7 catalog, remove the original
try {
mediaPackage.remove(segmentCatalog);
workspace.delete(segmentCatalog.getURI());
} catch (Exception e) {
logger.warn("Unable to delete segment catalog {}: {}", segmentCatalog.getURI(), e);
}
// Add flavor and target tags
catalog.setFlavor(MediaPackageElements.TEXTS);
for (String tag : targetTagSet) {
catalog.addTag(tag);
}
} finally {
// Remove images that were created for text extraction
logger.debug("Removing temporary images");
for (Attachment image : images) {
try {
workspace.delete(image.getURI());
} catch (Exception e) {
logger.warn("Unable to delete temporary image {}: {}", image.getURI(), e);
}
}
// Remove the temporary text
for (Job j : jobs.values()) {
Catalog catalog = null;
try {
Job job = serviceRegistry.getJob(j.getId());
if (!Job.Status.FINISHED.equals(job.getStatus()))
continue;
catalog = (Catalog) MediaPackageElementParser.getFromXml(job.getPayload());
if (catalog != null)
workspace.delete(catalog.getURI());
} catch (Exception e) {
if (catalog != null) {
logger.warn("Unable to delete temporary text file {}: {}", catalog.getURI(), e);
} else {
logger.warn("Unable to parse textextraction payload of job {}", j.getId());
}
}
}
}
}
logger.debug("Text analysis completed");
return createResult(mediaPackage, Action.CONTINUE, totalTimeInQueue);
}
Aggregations