use of org.opencastproject.mediapackage.MediaPackageException in project opencast by opencast.
the class TimelinePreviewsWorkflowOperationHandler method start.
/**
* {@inheritDoc}
*
* @see
* org.opencastproject.workflow.api.WorkflowOperationHandler#start(org.opencastproject.workflow.api.WorkflowInstance,
* org.opencastproject.job.api.JobContext)
*/
@Override
public WorkflowOperationResult start(WorkflowInstance workflowInstance, JobContext context) throws WorkflowOperationException {
MediaPackage mediaPackage = workflowInstance.getMediaPackage();
logger.info("Start timeline previews workflow operation for mediapackage {}", mediaPackage.getIdentifier().compact());
String sourceFlavorProperty = StringUtils.trimToNull(workflowInstance.getCurrentOperation().getConfiguration(SOURCE_FLAVOR_PROPERTY));
String sourceTagsProperty = StringUtils.trimToNull(workflowInstance.getCurrentOperation().getConfiguration(SOURCE_TAGS_PROPERTY));
if (StringUtils.isEmpty(sourceFlavorProperty) && StringUtils.isEmpty(sourceTagsProperty)) {
throw new WorkflowOperationException(String.format("Required property %s or %s not set", SOURCE_FLAVOR_PROPERTY, SOURCE_TAGS_PROPERTY));
}
String targetFlavorProperty = StringUtils.trimToNull(workflowInstance.getCurrentOperation().getConfiguration(TARGET_FLAVOR_PROPERTY));
if (targetFlavorProperty == null) {
throw new WorkflowOperationException(String.format("Required property %s not set", TARGET_FLAVOR_PROPERTY));
}
String targetTagsProperty = StringUtils.trimToNull(workflowInstance.getCurrentOperation().getConfiguration(TARGET_TAGS_PROPERTY));
String imageSizeArg = StringUtils.trimToNull(workflowInstance.getCurrentOperation().getConfiguration(IMAGE_SIZE_PROPERTY));
int imageSize;
if (imageSizeArg != null) {
try {
imageSize = Integer.parseInt(imageSizeArg);
} catch (NumberFormatException e) {
imageSize = DEFAULT_IMAGE_SIZE;
logger.info("No valid integer given for property {}, using default value: {}", IMAGE_SIZE_PROPERTY, DEFAULT_IMAGE_SIZE);
}
} else {
imageSize = DEFAULT_IMAGE_SIZE;
logger.info("Property {} not set, using default value: {}", IMAGE_SIZE_PROPERTY, DEFAULT_IMAGE_SIZE);
}
TrackSelector trackSelector = new TrackSelector();
for (String flavor : asList(sourceFlavorProperty)) {
trackSelector.addFlavor(flavor);
}
for (String tag : asList(sourceTagsProperty)) {
trackSelector.addTag(tag);
}
Collection<Track> sourceTracks = trackSelector.select(mediaPackage, false);
if (sourceTracks.isEmpty()) {
logger.info("No tracks found in mediapackage {} with specified {} {}", mediaPackage.getIdentifier().compact(), SOURCE_FLAVOR_PROPERTY, sourceFlavorProperty);
createResult(mediaPackage, WorkflowOperationResult.Action.SKIP);
}
List<Job> timelinepreviewsJobs = new ArrayList<Job>(sourceTracks.size());
for (Track sourceTrack : sourceTracks) {
try {
// generate timeline preview images
logger.info("Create timeline previews job for track '{}' in mediapackage '{}'", sourceTrack.getIdentifier(), mediaPackage.getIdentifier().compact());
Job timelinepreviewsJob = timelinePreviewsService.createTimelinePreviewImages(sourceTrack, imageSize);
timelinepreviewsJobs.add(timelinepreviewsJob);
} catch (MediaPackageException | TimelinePreviewsException ex) {
logger.error("Creating timeline previews job for track '{}' in media package '{}' failed with error {}", sourceTrack.getIdentifier(), mediaPackage.getIdentifier().compact(), ex.getMessage());
}
}
logger.info("Wait for timeline previews jobs for media package {}", mediaPackage.getIdentifier().compact());
if (!waitForStatus(timelinepreviewsJobs.toArray(new Job[timelinepreviewsJobs.size()])).isSuccess()) {
cleanupWorkspace(timelinepreviewsJobs);
throw new WorkflowOperationException(String.format("Timeline previews jobs for media package '%s' have not completed successfully", mediaPackage.getIdentifier().compact()));
}
try {
// copy timeline previews attachments into workspace and add them to the media package
for (Job job : timelinepreviewsJobs) {
String jobPayload = job.getPayload();
if (StringUtils.isNotEmpty(jobPayload)) {
MediaPackageElement timelinePreviewsMpe = null;
File timelinePreviewsFile = null;
try {
timelinePreviewsMpe = MediaPackageElementParser.getFromXml(jobPayload);
timelinePreviewsFile = workspace.get(timelinePreviewsMpe.getURI());
} catch (MediaPackageException ex) {
// unexpected job payload
throw new WorkflowOperationException("Can't parse timeline previews attachment from job " + job.getId());
} catch (NotFoundException ex) {
throw new WorkflowOperationException("Timeline preview images file '" + timelinePreviewsMpe.getURI() + "' not found", ex);
} catch (IOException ex) {
throw new WorkflowOperationException("Can't get workflow image file '" + timelinePreviewsMpe.getURI() + "' from workspace");
}
FileInputStream timelinePreviewsInputStream = null;
logger.info("Put timeline preview images file {} from media package {} to the media package work space", timelinePreviewsMpe.getURI(), mediaPackage.getIdentifier().compact());
try {
timelinePreviewsInputStream = new FileInputStream(timelinePreviewsFile);
String fileName = FilenameUtils.getName(timelinePreviewsMpe.getURI().getPath());
URI timelinePreviewsWfrUri = workspace.put(mediaPackage.getIdentifier().compact(), timelinePreviewsMpe.getIdentifier(), fileName, timelinePreviewsInputStream);
timelinePreviewsMpe.setURI(timelinePreviewsWfrUri);
} catch (FileNotFoundException ex) {
throw new WorkflowOperationException("Timeline preview images file " + timelinePreviewsFile.getPath() + " not found", ex);
} catch (IOException ex) {
throw new WorkflowOperationException("Can't read just created timeline preview images file " + timelinePreviewsFile.getPath(), ex);
} catch (IllegalArgumentException ex) {
throw new WorkflowOperationException(ex);
} finally {
IoSupport.closeQuietly(timelinePreviewsInputStream);
}
// set the timeline previews attachment flavor and add it to the mediapackage
MediaPackageElementFlavor targetFlavor = MediaPackageElementFlavor.parseFlavor(targetFlavorProperty);
if ("*".equals(targetFlavor.getType())) {
targetFlavor = new MediaPackageElementFlavor(timelinePreviewsMpe.getFlavor().getType(), targetFlavor.getSubtype());
}
if ("*".equals(targetFlavor.getSubtype())) {
targetFlavor = new MediaPackageElementFlavor(targetFlavor.getType(), timelinePreviewsMpe.getFlavor().getSubtype());
}
timelinePreviewsMpe.setFlavor(targetFlavor);
if (!StringUtils.isEmpty(targetTagsProperty)) {
for (String tag : asList(targetTagsProperty)) {
timelinePreviewsMpe.addTag(tag);
}
}
mediaPackage.add(timelinePreviewsMpe);
}
}
} finally {
cleanupWorkspace(timelinepreviewsJobs);
}
logger.info("Timeline previews workflow operation for mediapackage {} completed", mediaPackage.getIdentifier().compact());
return createResult(mediaPackage, WorkflowOperationResult.Action.CONTINUE);
}
use of org.opencastproject.mediapackage.MediaPackageException in project opencast by opencast.
the class SoxServiceRemoteImpl method analyze.
/**
* {@inheritDoc}
*
* @see org.opencastproject.sox.api.SoxService#analyze(Track)
*/
@Override
public Job analyze(Track sourceAudioTrack) throws MediaPackageException, SoxException {
HttpPost post = new HttpPost("/analyze");
try {
List<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>();
params.add(new BasicNameValuePair("sourceAudioTrack", MediaPackageElementParser.getAsXml(sourceAudioTrack)));
post.setEntity(new UrlEncodedFormEntity(params));
} catch (Exception e) {
throw new SoxException(e);
}
HttpResponse response = null;
try {
response = getResponse(post);
if (response != null) {
try {
Job receipt = JobParser.parseJob(response.getEntity().getContent());
logger.info("Analyzing audio {} on a remote analysis server", sourceAudioTrack);
return receipt;
} catch (Exception e) {
throw new SoxException("Unable to analyze audio of element '" + sourceAudioTrack + "' using a remote analysis service", e);
}
}
} finally {
closeConnection(response);
}
throw new SoxException("Unable to analyze audio of element '" + sourceAudioTrack + "' using a remote analysis service");
}
use of org.opencastproject.mediapackage.MediaPackageException 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);
}
use of org.opencastproject.mediapackage.MediaPackageException in project opencast by opencast.
the class TimelinePreviewsServiceImpl method generatePreviewImages.
/**
* Starts generation of timeline preview images for the given video track
* and returns an attachment containing one image that contains all the
* timeline preview images.
*
* @param job
* @param track the element to analyze
* @param imageCount number of preview images that will be generated
* @return an attachment containing the resulting timeline previews image
* @throws TimelinePreviewsException
* @throws org.opencastproject.mediapackage.MediaPackageException
*/
protected Attachment generatePreviewImages(Job job, Track track, int imageCount) throws TimelinePreviewsException, MediaPackageException {
// Make sure the element can be analyzed using this analysis implementation
if (!track.hasVideo()) {
logger.error("Element {} is not a video track", track.getIdentifier());
throw new TimelinePreviewsException("Element is not a video track");
}
try {
if (track.getDuration() == null)
throw new MediaPackageException("Track " + track + " does not have a duration");
double duration = track.getDuration() / 1000.0;
double seconds = duration / (double) (imageCount);
seconds = seconds <= 0.0 ? 1.0 : seconds;
// calculate number of tiles for row and column in tiled image
int imageSize = (int) Math.ceil(Math.sqrt(imageCount));
Attachment composedImage = createPreviewsFFmpeg(track, seconds, resolutionX, resolutionY, imageSize, imageSize, duration);
if (composedImage == null)
throw new IllegalStateException("Unable to compose image");
// Set the mimetype
try {
composedImage.setMimeType(MimeTypes.parseMimeType(mimetype));
} catch (IllegalArgumentException e) {
logger.warn("Invalid mimetype provided for timeline previews image");
try {
composedImage.setMimeType(MimeTypes.fromURI(composedImage.getURI()));
} catch (UnknownFileTypeException ex) {
logger.warn("No valid mimetype could be found for timeline previews image");
}
}
composedImage.getProperties().put("imageCount", String.valueOf(imageCount));
return composedImage;
} catch (Exception e) {
logger.warn("Error creating timeline preview images for " + track, e);
if (e instanceof TimelinePreviewsException) {
throw (TimelinePreviewsException) e;
} else {
throw new TimelinePreviewsException(e);
}
}
}
use of org.opencastproject.mediapackage.MediaPackageException in project opencast by opencast.
the class SchedulerServiceImpl method updateDublincCoreCatalog.
/**
* @param mp
* the mediapackage to update
* @param dc
* the dublincore metadata to use to update the mediapackage
* @return the updated mediapackage
* @throws IOException
* Thrown if an IO error occurred adding the dc catalog file
* @throws MediaPackageException
* Thrown if an error occurred updating the mediapackage or the mediapackage does not contain a catalog
*/
private MediaPackage updateDublincCoreCatalog(MediaPackage mp, DublinCoreCatalog dc) throws IOException, MediaPackageException {
try (InputStream inputStream = IOUtils.toInputStream(dc.toXmlString(), "UTF-8")) {
// Update dublincore catalog
Catalog[] catalogs = mp.getCatalogs(MediaPackageElements.EPISODE);
if (catalogs.length > 0) {
Catalog catalog = catalogs[0];
URI uri = workspace.put(mp.getIdentifier().toString(), catalog.getIdentifier(), "dublincore.xml", inputStream);
catalog.setURI(uri);
// setting the URI to a new source so the checksum will most like be invalid
catalog.setChecksum(null);
} else {
throw new MediaPackageException("Unable to find catalog");
}
}
return mp;
}
Aggregations