Search in sources :

Example 1 with Dimension

use of org.opencastproject.composer.layout.Dimension in project opencast by opencast.

the class PartialImportWorkflowOperationHandler method getLargestTrack.

/**
 * Returns the track with the largest resolution from the list of tracks
 *
 * @param tracks
 *          the list of tracks
 * @return a {@link Tuple} with the largest track and it's dimension
 */
private Tuple<Track, Dimension> getLargestTrack(List<Track> tracks) {
    Track track = null;
    Dimension dimension = null;
    for (Track t : tracks) {
        if (!t.hasVideo())
            continue;
        VideoStream[] videoStreams = TrackSupport.byType(t.getStreams(), VideoStream.class);
        int frameWidth = videoStreams[0].getFrameWidth();
        int frameHeight = videoStreams[0].getFrameHeight();
        if (dimension == null || (frameWidth * frameHeight) > (dimension.getWidth() * dimension.getHeight())) {
            dimension = Dimension.dimension(frameWidth, frameHeight);
            track = t;
        }
    }
    if (track == null || dimension == null)
        return null;
    return Tuple.tuple(track, dimension);
}
Also used : VideoStream(org.opencastproject.mediapackage.VideoStream) Dimension(org.opencastproject.composer.layout.Dimension) Track(org.opencastproject.mediapackage.Track)

Example 2 with Dimension

use of org.opencastproject.composer.layout.Dimension in project opencast by opencast.

the class ComposerRestService method composite.

/**
 * Compose two videos into one with an optional watermark.
 *
 * @param compositeSizeJson
 *          The composite track dimension as JSON
 * @param lowerTrackXml
 *          The lower track of the composition as XML
 * @param lowerLayoutJson
 *          The lower layout as JSON
 * @param upperTrackXml
 *          The upper track of the composition as XML
 * @param upperLayoutJson
 *          The upper layout as JSON
 * @param watermarkAttachmentXml
 *          The watermark image attachment of the composition as XML
 * @param watermarkLayoutJson
 *          The watermark layout as JSON
 * @param profileId
 *          The encoding profile to use
 * @param background
 *          The background color
 * @return A {@link Response} with the resulting track in the response body
 * @throws Exception
 */
@POST
@Path("composite")
@Produces(MediaType.TEXT_XML)
@RestQuery(name = "composite", description = "Starts a video compositing process, based on the specified resolution, encoding profile ID, the source elements and their layouts", restParameters = { @RestParameter(description = "The resolution size of the resulting video as JSON", isRequired = true, name = "compositeSize", type = Type.STRING), @RestParameter(description = "The lower source track containing the lower video", isRequired = true, name = "lowerTrack", type = Type.TEXT), @RestParameter(description = "The lower layout containing the JSON definition of the layout", isRequired = true, name = "lowerLayout", type = Type.TEXT), @RestParameter(description = "The upper source track containing the upper video", isRequired = false, name = "upperTrack", type = Type.TEXT), @RestParameter(description = "The upper layout containing the JSON definition of the layout", isRequired = false, name = "upperLayout", type = Type.TEXT), @RestParameter(description = "The watermark source attachment containing watermark image", isRequired = false, name = "watermarkTrack", type = Type.TEXT), @RestParameter(description = "The watermark layout containing the JSON definition of the layout", isRequired = false, name = "watermarkLayout", type = Type.TEXT), @RestParameter(description = "The background color", isRequired = false, name = "background", type = Type.TEXT, defaultValue = "black"), @RestParameter(description = "The encoding profile to use", isRequired = true, name = "profileId", type = Type.STRING) }, reponses = { @RestResponse(description = "Results in an xml document containing the compound video track", responseCode = HttpServletResponse.SC_OK), @RestResponse(description = "If required parameters aren't set or if the source elements aren't from the right type", responseCode = HttpServletResponse.SC_BAD_REQUEST) }, returnDescription = "")
public Response composite(@FormParam("compositeSize") String compositeSizeJson, @FormParam("lowerTrack") String lowerTrackXml, @FormParam("lowerLayout") String lowerLayoutJson, @FormParam("upperTrack") String upperTrackXml, @FormParam("upperLayout") String upperLayoutJson, @FormParam("watermarkAttachment") String watermarkAttachmentXml, @FormParam("watermarkLayout") String watermarkLayoutJson, @FormParam("profileId") String profileId, @FormParam("background") @DefaultValue("black") String background) throws Exception {
    // Ensure that the POST parameters are present
    if (StringUtils.isBlank(compositeSizeJson) || StringUtils.isBlank(lowerTrackXml) || StringUtils.isBlank(lowerLayoutJson) || StringUtils.isBlank(profileId))
        return Response.status(Response.Status.BAD_REQUEST).entity("One of the required parameters must not be null").build();
    // Deserialize the source elements
    MediaPackageElement lowerTrack = MediaPackageElementParser.getFromXml(lowerTrackXml);
    Layout lowerLayout = Serializer.layout(JsonObj.jsonObj(lowerLayoutJson));
    if (!Track.TYPE.equals(lowerTrack.getElementType()))
        return Response.status(Response.Status.BAD_REQUEST).entity("lowerTrack element must be of type track").build();
    LaidOutElement<Track> lowerLaidOutElement = new LaidOutElement<Track>((Track) lowerTrack, lowerLayout);
    Option<LaidOutElement<Track>> upperLaidOutElement = Option.<LaidOutElement<Track>>none();
    if (StringUtils.isNotBlank(upperTrackXml)) {
        MediaPackageElement upperTrack = MediaPackageElementParser.getFromXml(upperTrackXml);
        Layout upperLayout = Serializer.layout(JsonObj.jsonObj(upperLayoutJson));
        if (!Track.TYPE.equals(upperTrack.getElementType())) {
            return Response.status(Response.Status.BAD_REQUEST).entity("upperTrack element must be of type track").build();
        }
        upperLaidOutElement = Option.option(new LaidOutElement<Track>((Track) upperTrack, upperLayout));
    }
    Option<LaidOutElement<Attachment>> watermarkLaidOutElement = Option.<LaidOutElement<Attachment>>none();
    if (StringUtils.isNotBlank(watermarkAttachmentXml)) {
        Layout watermarkLayout = Serializer.layout(JsonObj.jsonObj(watermarkLayoutJson));
        MediaPackageElement watermarkAttachment = MediaPackageElementParser.getFromXml(watermarkAttachmentXml);
        if (!Attachment.TYPE.equals(watermarkAttachment.getElementType()))
            return Response.status(Response.Status.BAD_REQUEST).entity("watermarkTrack element must be of type track").build();
        watermarkLaidOutElement = Option.some(new LaidOutElement<Attachment>((Attachment) watermarkAttachment, watermarkLayout));
    }
    Dimension compositeTrackSize = Serializer.dimension(JsonObj.jsonObj(compositeSizeJson));
    try {
        // Asynchronously composite the specified source elements
        Job job = composerService.composite(compositeTrackSize, upperLaidOutElement, lowerLaidOutElement, watermarkLaidOutElement, profileId, background);
        return Response.ok().entity(new JaxbJob(job)).build();
    } catch (EncoderException e) {
        logger.warn("Unable to composite video: " + e.getMessage());
        return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
    }
}
Also used : EncoderException(org.opencastproject.composer.api.EncoderException) Layout(org.opencastproject.composer.layout.Layout) MediaPackageElement(org.opencastproject.mediapackage.MediaPackageElement) JaxbJob(org.opencastproject.job.api.JaxbJob) LaidOutElement(org.opencastproject.composer.api.LaidOutElement) Dimension(org.opencastproject.composer.layout.Dimension) JaxbJob(org.opencastproject.job.api.JaxbJob) Job(org.opencastproject.job.api.Job) Track(org.opencastproject.mediapackage.Track) Path(javax.ws.rs.Path) POST(javax.ws.rs.POST) Produces(javax.ws.rs.Produces) RestQuery(org.opencastproject.util.doc.rest.RestQuery)

Example 3 with Dimension

use of org.opencastproject.composer.layout.Dimension in project opencast by opencast.

the class ComposerServiceTest method testComposite.

/**
 * Test method for
 * {@link ComposerServiceImpl#composite(Dimension, Option, LaidOutElement, Option, String, String)}
 */
@Test
public void testComposite() throws Exception {
    if (!ffmpegInstalled)
        return;
    Dimension outputDimension = new Dimension(500, 500);
    List<HorizontalCoverageLayoutSpec> layouts = new ArrayList<HorizontalCoverageLayoutSpec>();
    layouts.add(Serializer.horizontalCoverageLayoutSpec(JsonObj.jsonObj("{\"horizontalCoverage\":1.0,\"anchorOffset\":{\"referring\":{\"left\":1.0,\"top\":1.0},\"offset\":{\"y\":-20,\"x\":-20},\"reference\":{\"left\":1.0,\"top\":1.0}}}")));
    layouts.add(Serializer.horizontalCoverageLayoutSpec(JsonObj.jsonObj("{\"horizontalCoverage\":0.2,\"anchorOffset\":{\"referring\":{\"left\":0.0,\"top\":0.0},\"offset\":{\"y\":-20,\"x\":-20},\"reference\":{\"left\":0.0,\"top\":0.0}}}")));
    layouts.add(Serializer.horizontalCoverageLayoutSpec(JsonObj.jsonObj("{\"horizontalCoverage\":1.0,\"anchorOffset\":{\"referring\":{\"left\":1.0,\"top\":0.0},\"offset\":{\"y\":20,\"x\":20},\"reference\":{\"left\":1.0,\"top\":0.0}}}")));
    List<Tuple<Dimension, HorizontalCoverageLayoutSpec>> shapes = new ArrayList<>();
    shapes.add(0, Tuple.tuple(new Dimension(300, 300), layouts.get(0)));
    shapes.add(1, Tuple.tuple(new Dimension(200, 200), layouts.get(1)));
    MultiShapeLayout multiShapeLayout = LayoutManager.multiShapeLayout(outputDimension, shapes);
    Option<LaidOutElement<Attachment>> watermarkOption = Option.<LaidOutElement<Attachment>>none();
    LaidOutElement<Track> lowerLaidOutElement = new LaidOutElement<Track>(sourceVideoTrack, multiShapeLayout.getShapes().get(0));
    LaidOutElement<Track> upperLaidOutElement = new LaidOutElement<Track>(sourceVideoTrack, multiShapeLayout.getShapes().get(1));
    Job composite = composerService.composite(outputDimension, Option.option(lowerLaidOutElement), upperLaidOutElement, watermarkOption, "composite.work", "black");
    Track compositeTrack = (Track) MediaPackageElementParser.getFromXml(composite.getPayload());
    Assert.assertNotNull(compositeTrack);
    inspectedTrack.setIdentifier(compositeTrack.getIdentifier());
    inspectedTrack.setMimeType(MimeType.mimeType("video", "mp4"));
    Assert.assertEquals(inspectedTrack, compositeTrack);
}
Also used : MultiShapeLayout(org.opencastproject.composer.layout.MultiShapeLayout) ArrayList(java.util.ArrayList) LaidOutElement(org.opencastproject.composer.api.LaidOutElement) Dimension(org.opencastproject.composer.layout.Dimension) HorizontalCoverageLayoutSpec(org.opencastproject.composer.layout.HorizontalCoverageLayoutSpec) Job(org.opencastproject.job.api.Job) Tuple(org.opencastproject.util.data.Tuple) Track(org.opencastproject.mediapackage.Track) Test(org.junit.Test)

Example 4 with Dimension

use of org.opencastproject.composer.layout.Dimension in project opencast by opencast.

the class CompositeWorkflowOperationHandler method createWatermarkLaidOutElement.

private Option<LaidOutElement<Attachment>> createWatermarkLaidOutElement(CompositeSettings compositeSettings, Dimension outputDimension, Option<Attachment> watermarkAttachment) throws WorkflowOperationException {
    Option<LaidOutElement<Attachment>> watermarkOption = Option.<LaidOutElement<Attachment>>none();
    if (watermarkAttachment.isSome() && compositeSettings.getWatermarkLayout().isSome()) {
        BufferedImage image;
        try {
            File watermarkFile = workspace.get(watermarkAttachment.get().getURI());
            image = ImageIO.read(watermarkFile);
        } catch (Exception e) {
            logger.warn("Unable to read the watermark image attachment {}: {}", watermarkAttachment.get().getURI(), e);
            throw new WorkflowOperationException("Unable to read the watermark image attachment", e);
        }
        Dimension imageDimension = Dimension.dimension(image.getWidth(), image.getHeight());
        List<Tuple<Dimension, AbsolutePositionLayoutSpec>> watermarkShapes = new ArrayList<Tuple<Dimension, AbsolutePositionLayoutSpec>>();
        watermarkShapes.add(0, Tuple.tuple(imageDimension, compositeSettings.getWatermarkLayout().get()));
        MultiShapeLayout watermarkLayout = LayoutManager.absoluteMultiShapeLayout(outputDimension, watermarkShapes);
        watermarkOption = Option.some(new LaidOutElement<Attachment>(watermarkAttachment.get(), watermarkLayout.getShapes().get(0)));
    }
    return watermarkOption;
}
Also used : MultiShapeLayout(org.opencastproject.composer.layout.MultiShapeLayout) WorkflowOperationException(org.opencastproject.workflow.api.WorkflowOperationException) ArrayList(java.util.ArrayList) LaidOutElement(org.opencastproject.composer.api.LaidOutElement) Dimension(org.opencastproject.composer.layout.Dimension) File(java.io.File) BufferedImage(java.awt.image.BufferedImage) WorkflowOperationException(org.opencastproject.workflow.api.WorkflowOperationException) MediaPackageException(org.opencastproject.mediapackage.MediaPackageException) NotFoundException(org.opencastproject.util.NotFoundException) IOException(java.io.IOException) EncoderException(org.opencastproject.composer.api.EncoderException) Tuple(org.opencastproject.util.data.Tuple) AbsolutePositionLayoutSpec(org.opencastproject.composer.layout.AbsolutePositionLayoutSpec)

Example 5 with Dimension

use of org.opencastproject.composer.layout.Dimension in project opencast by opencast.

the class CompositeWorkflowOperationHandler method handleMultipleTracks.

private WorkflowOperationResult handleMultipleTracks(MediaPackage mediaPackage, WorkflowOperationInstance operation, CompositeSettings compositeSettings, Option<Attachment> watermarkAttachment) throws EncoderException, IOException, NotFoundException, MediaPackageException, WorkflowOperationException {
    if (compositeSettings.getMultiSourceLayouts() == null || compositeSettings.getMultiSourceLayouts().size() == 0) {
        throw new WorkflowOperationException("Multi video layout must be set! Please verify that you have a " + LAYOUT_MULTIPLE + " or " + LAYOUT + " property in your composite operation in your workflow definition to be able to handle multiple videos");
    }
    try {
        Track upperTrack = compositeSettings.getUpperTrack();
        Track lowerTrack = compositeSettings.getLowerTrack();
        List<HorizontalCoverageLayoutSpec> layouts = compositeSettings.getMultiSourceLayouts();
        VideoStream[] upperVideoStreams = TrackSupport.byType(upperTrack.getStreams(), VideoStream.class);
        if (upperVideoStreams.length == 0) {
            logger.warn("No video stream available in the upper track! {}", upperTrack);
            return createResult(mediaPackage, Action.SKIP);
        }
        VideoStream[] lowerVideoStreams = TrackSupport.byType(lowerTrack.getStreams(), VideoStream.class);
        if (lowerVideoStreams.length == 0) {
            logger.warn("No video stream available in the lower track! {}", lowerTrack);
            return createResult(mediaPackage, Action.SKIP);
        }
        // Read the video dimensions from the mediapackage stream information
        Dimension upperDimensions = Dimension.dimension(upperVideoStreams[0].getFrameWidth(), upperVideoStreams[0].getFrameHeight());
        Dimension lowerDimensions = Dimension.dimension(lowerVideoStreams[0].getFrameWidth(), lowerVideoStreams[0].getFrameHeight());
        // Determine dimension of output
        Dimension outputDimension = null;
        String outputResolutionSource = compositeSettings.getOutputResolutionSource();
        if (outputResolutionSource.equals(CompositeSettings.OUTPUT_RESOLUTION_FIXED)) {
            outputDimension = compositeSettings.getOutputDimension();
        } else if (outputResolutionSource.equals(CompositeSettings.OUTPUT_RESOLUTION_LOWER)) {
            outputDimension = lowerDimensions;
        } else if (outputResolutionSource.equals(CompositeSettings.OUTPUT_RESOLUTION_UPPER)) {
            outputDimension = upperDimensions;
        }
        // Create the video layout definitions
        List<Tuple<Dimension, HorizontalCoverageLayoutSpec>> shapes = new ArrayList<Tuple<Dimension, HorizontalCoverageLayoutSpec>>();
        shapes.add(0, Tuple.tuple(lowerDimensions, layouts.get(0)));
        shapes.add(1, Tuple.tuple(upperDimensions, layouts.get(1)));
        // Calculate the layout
        MultiShapeLayout multiShapeLayout = LayoutManager.multiShapeLayout(outputDimension, shapes);
        // Create the laid out element for the videos
        LaidOutElement<Track> lowerLaidOutElement = new LaidOutElement<Track>(lowerTrack, multiShapeLayout.getShapes().get(0));
        LaidOutElement<Track> upperLaidOutElement = new LaidOutElement<Track>(upperTrack, multiShapeLayout.getShapes().get(1));
        // Create the optionally laid out element for the watermark
        Option<LaidOutElement<Attachment>> watermarkOption = createWatermarkLaidOutElement(compositeSettings, outputDimension, watermarkAttachment);
        Job compositeJob = composerService.composite(outputDimension, Option.option(upperLaidOutElement), lowerLaidOutElement, watermarkOption, compositeSettings.getProfile().getIdentifier(), compositeSettings.getOutputBackground());
        // Wait for the jobs to return
        if (!waitForStatus(compositeJob).isSuccess())
            throw new WorkflowOperationException("The composite job did not complete successfully");
        if (compositeJob.getPayload().length() > 0) {
            Track compoundTrack = (Track) MediaPackageElementParser.getFromXml(compositeJob.getPayload());
            compoundTrack.setURI(workspace.moveTo(compoundTrack.getURI(), mediaPackage.getIdentifier().toString(), compoundTrack.getIdentifier(), "composite." + FilenameUtils.getExtension(compoundTrack.getURI().toString())));
            // Adjust the target tags
            for (String tag : compositeSettings.getTargetTags()) {
                logger.trace("Tagging compound track with '{}'", tag);
                compoundTrack.addTag(tag);
            }
            // Adjust the target flavor.
            compoundTrack.setFlavor(compositeSettings.getTargetFlavor());
            logger.debug("Compound track has flavor '{}'", compoundTrack.getFlavor());
            // store new tracks to mediaPackage
            mediaPackage.add(compoundTrack);
            WorkflowOperationResult result = createResult(mediaPackage, Action.CONTINUE, compositeJob.getQueueTime());
            logger.debug("Composite operation completed");
            return result;
        } else {
            logger.info("Composite operation unsuccessful, no payload returned: {}", compositeJob);
            return createResult(mediaPackage, Action.SKIP);
        }
    } finally {
        if (compositeSettings.getSourceUrlWatermark() != null)
            workspace.deleteFromCollection(COLLECTION, compositeSettings.getWatermarkIdentifier() + "." + FilenameUtils.getExtension(compositeSettings.getSourceUrlWatermark()));
    }
}
Also used : MultiShapeLayout(org.opencastproject.composer.layout.MultiShapeLayout) VideoStream(org.opencastproject.mediapackage.VideoStream) ArrayList(java.util.ArrayList) Dimension(org.opencastproject.composer.layout.Dimension) WorkflowOperationResult(org.opencastproject.workflow.api.WorkflowOperationResult) WorkflowOperationException(org.opencastproject.workflow.api.WorkflowOperationException) LaidOutElement(org.opencastproject.composer.api.LaidOutElement) HorizontalCoverageLayoutSpec(org.opencastproject.composer.layout.HorizontalCoverageLayoutSpec) Job(org.opencastproject.job.api.Job) Track(org.opencastproject.mediapackage.Track) Tuple(org.opencastproject.util.data.Tuple)

Aggregations

Dimension (org.opencastproject.composer.layout.Dimension)14 Track (org.opencastproject.mediapackage.Track)11 Job (org.opencastproject.job.api.Job)8 ArrayList (java.util.ArrayList)7 LaidOutElement (org.opencastproject.composer.api.LaidOutElement)6 EncoderException (org.opencastproject.composer.api.EncoderException)5 Tuple (org.opencastproject.util.data.Tuple)5 Test (org.junit.Test)4 MultiShapeLayout (org.opencastproject.composer.layout.MultiShapeLayout)4 VideoStream (org.opencastproject.mediapackage.VideoStream)4 WorkflowOperationException (org.opencastproject.workflow.api.WorkflowOperationException)4 IOException (java.io.IOException)3 HorizontalCoverageLayoutSpec (org.opencastproject.composer.layout.HorizontalCoverageLayoutSpec)3 MediaPackageException (org.opencastproject.mediapackage.MediaPackageException)3 NotFoundException (org.opencastproject.util.NotFoundException)3 WorkflowOperationResult (org.opencastproject.workflow.api.WorkflowOperationResult)3 POST (javax.ws.rs.POST)2 Path (javax.ws.rs.Path)2 Produces (javax.ws.rs.Produces)2 EncodingProfile (org.opencastproject.composer.api.EncodingProfile)2