Search in sources :

Example 26 with Track

use of org.opencastproject.mediapackage.Track in project opencast by opencast.

the class CompositeWorkflowOperationHandlerTest method testSingleLayout.

@Test
public void testSingleLayout() throws Exception {
    setMockups();
    // operation configuration
    String targetTags = "engage,compound";
    Map<String, String> configurations = new HashMap<String, String>();
    configurations.put("source-flavor-upper", "presenter/source");
    configurations.put("source-flavor-lower", "presentation/source");
    configurations.put("target-tags", targetTags);
    configurations.put("target-flavor", "composite/work");
    configurations.put("encoding-profile", "composite");
    configurations.put("layout", TEST_LAYOUT);
    configurations.put("layout-single", TEST_SINGLE_LAYOUT);
    configurations.put("output-resolution", "1900x1080");
    configurations.put("output-background", "black");
    // run the operation handler
    WorkflowOperationResult result = getWorkflowOperationResult(mp, configurations);
    // check track metadata
    MediaPackage mpNew = result.getMediaPackage();
    Assert.assertEquals(Action.CONTINUE, result.getAction());
    Track trackEncoded = mpNew.getTrack(COMPOUND_TRACK_ID);
    Assert.assertEquals("composite/work", trackEncoded.getFlavor().toString());
    Assert.assertTrue(Arrays.asList(targetTags.split("\\W")).containsAll(Arrays.asList(trackEncoded.getTags())));
}
Also used : HashMap(java.util.HashMap) MediaPackage(org.opencastproject.mediapackage.MediaPackage) WorkflowOperationResult(org.opencastproject.workflow.api.WorkflowOperationResult) Track(org.opencastproject.mediapackage.Track) Test(org.junit.Test)

Example 27 with Track

use of org.opencastproject.mediapackage.Track in project opencast by opencast.

the class ComposerServiceImpl method encode.

/**
 * 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 tracks
 *          tracks to use for processing
 * @param profileId
 *          the encoding profile
 * @param properties
 *          encoding properties
 * @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 Option<Track> encode(final Job job, Map<String, Track> tracks, String profileId) throws EncoderException, MediaPackageException {
    final String targetTrackId = idBuilder.createNew().toString();
    Map<String, File> files = new HashMap<>();
    // Get the tracks and make sure they exist
    for (Entry<String, Track> track : tracks.entrySet()) {
        files.put(track.getKey(), loadTrackIntoWorkspace(job, track.getKey(), track.getValue()));
    }
    // Get the encoding profile
    final EncodingProfile profile = getProfile(job, profileId);
    List<String> trackMsg = new LinkedList<>();
    for (Entry<String, Track> track : tracks.entrySet()) {
        trackMsg.add(String.format("%s: %s", track.getKey(), track.getValue().getIdentifier()));
    }
    logger.info("Encoding {} into {} using profile {}", StringUtils.join(trackMsg, ", "), targetTrackId, profileId);
    // Do the work
    final EncoderEngine encoder = getEncoderEngine();
    List<File> output;
    try {
        output = encoder.process(files, profile, null);
    } catch (EncoderException e) {
        Map<String, String> params = new HashMap<>();
        for (Entry<String, Track> track : tracks.entrySet()) {
            params.put(track.getKey(), track.getValue().getIdentifier());
        }
        params.put("profile", profile.getIdentifier());
        params.put("properties", "EMPTY");
        incident().recordFailure(job, ENCODING_FAILED, e, params, detailsFor(e, encoder));
        throw e;
    } finally {
        activeEncoder.remove(encoder);
    }
    // We expect zero or one file as output
    if (output.size() == 0) {
        return none();
    } else if (output.size() != 1) {
        // Ensure we do not leave behind old files in the workspace
        for (File file : output) {
            FileUtils.deleteQuietly(file);
        }
        throw new EncoderException("Composite does not support multiple files as output");
    }
    // Put the file in the workspace
    URI workspaceURI = putToCollection(job, output.get(0), "encoded file");
    // Have the encoded track inspected and return the result
    Job inspectionJob = inspect(job, workspaceURI);
    Track inspectedTrack = (Track) MediaPackageElementParser.getFromXml(inspectionJob.getPayload());
    inspectedTrack.setIdentifier(targetTrackId);
    if (profile.getMimeType() != null)
        inspectedTrack.setMimeType(MimeTypes.parseMimeType(profile.getMimeType()));
    return some(inspectedTrack);
}
Also used : HashMap(java.util.HashMap) EncodingProfile(org.opencastproject.composer.api.EncodingProfile) URI(java.net.URI) LinkedList(java.util.LinkedList) EncoderException(org.opencastproject.composer.api.EncoderException) Entry(java.util.Map.Entry) Job(org.opencastproject.job.api.Job) File(java.io.File) Map(java.util.Map) HashMap(java.util.HashMap) Track(org.opencastproject.mediapackage.Track)

Example 28 with Track

use of org.opencastproject.mediapackage.Track in project opencast by opencast.

the class ComposerServiceImpl method concat.

private Option<Track> concat(Job job, List<Track> tracks, String profileId, Dimension outputDimension, float outputFrameRate) throws EncoderException, MediaPackageException {
    if (tracks.size() < 2) {
        Map<String, String> params = new HashMap<>();
        params.put("tracks-size", Integer.toString(tracks.size()));
        params.put("tracks", StringUtils.join(tracks, ","));
        incident().recordFailure(job, CONCAT_LESS_TRACKS, params);
        throw new EncoderException("The track parameter must at least have two tracks present");
    }
    boolean onlyAudio = true;
    for (Track t : tracks) {
        if (t.hasVideo()) {
            onlyAudio = false;
            break;
        }
    }
    if (!onlyAudio && outputDimension == null) {
        Map<String, String> params = new HashMap<>();
        params.put("tracks", StringUtils.join(tracks, ","));
        incident().recordFailure(job, CONCAT_NO_DIMENSION, params);
        throw new EncoderException("The output dimension id parameter must not be null when concatenating video");
    }
    final String targetTrackId = idBuilder.createNew().toString();
    // Get the tracks and make sure they exist
    List<File> trackFiles = new ArrayList<>();
    int i = 0;
    for (Track track : tracks) {
        if (!track.hasAudio() && !track.hasVideo()) {
            Map<String, String> params = new HashMap<>();
            params.put("track-id", track.getIdentifier());
            params.put("track-url", track.getURI().toString());
            incident().recordFailure(job, NO_STREAMS, params);
            throw new EncoderException("Track has no audio or video stream available: " + track);
        }
        trackFiles.add(i++, loadTrackIntoWorkspace(job, "concat", track));
    }
    // Create the engine
    final EncoderEngine encoderEngine = getEncoderEngine();
    if (onlyAudio) {
        logger.info("Concatenating audio tracks {} into {}", trackFiles, targetTrackId);
    } else {
        logger.info("Concatenating video tracks {} into {}", trackFiles, targetTrackId);
    }
    // Get the encoding profile
    EncodingProfile profile = getProfile(job, profileId);
    // Creating video filter command for concat
    final String concatCommand = buildConcatCommand(onlyAudio, outputDimension, outputFrameRate, trackFiles, tracks);
    Map<String, String> properties = new HashMap<>();
    properties.put(EncoderEngine.CMD_SUFFIX + ".concatCommand", concatCommand);
    File output;
    try {
        output = encoderEngine.encode(trackFiles.get(0), profile, properties);
    } catch (EncoderException e) {
        Map<String, String> params = new HashMap<>();
        List<String> trackList = new ArrayList<>();
        for (Track t : tracks) {
            trackList.add(t.getURI().toString());
        }
        params.put("tracks", StringUtils.join(trackList, ","));
        params.put("profile", profile.getIdentifier());
        params.put("properties", properties.toString());
        incident().recordFailure(job, CONCAT_FAILED, e, params, detailsFor(e, encoderEngine));
        throw e;
    } finally {
        activeEncoder.remove(encoderEngine);
    }
    // concat did not return a file
    if (!output.exists() || output.length() == 0)
        return none();
    // Put the file in the workspace
    URI workspaceURI = putToCollection(job, output, "concatenated file");
    // Have the concat track inspected and return the result
    Job inspectionJob = inspect(job, workspaceURI);
    Track inspectedTrack = (Track) MediaPackageElementParser.getFromXml(inspectionJob.getPayload());
    inspectedTrack.setIdentifier(targetTrackId);
    if (profile.getMimeType() != null)
        inspectedTrack.setMimeType(MimeTypes.parseMimeType(profile.getMimeType()));
    return some(inspectedTrack);
}
Also used : HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) EncodingProfile(org.opencastproject.composer.api.EncodingProfile) URI(java.net.URI) EncoderException(org.opencastproject.composer.api.EncoderException) List(java.util.List) ArrayList(java.util.ArrayList) LinkedList(java.util.LinkedList) Job(org.opencastproject.job.api.Job) File(java.io.File) Map(java.util.Map) HashMap(java.util.HashMap) Track(org.opencastproject.mediapackage.Track)

Example 29 with Track

use of org.opencastproject.mediapackage.Track in project opencast by opencast.

the class ComposerRestService method image.

/**
 * Encodes a track in a media package.
 *
 * @param sourceTrackXml
 *          The source track
 * @param profileId
 *          The profile to use in encoding this track
 * @param times
 *          one or more times in seconds separated by comma
 * @return A {@link Response} with the resulting track in the response body
 * @throws Exception
 */
@POST
@Path("image")
@Produces(MediaType.TEXT_XML)
@RestQuery(name = "image", description = "Starts an image extraction process, based on the specified encoding profile ID and the source track", restParameters = { @RestParameter(description = "The track containing the video stream", isRequired = true, name = "sourceTrack", type = Type.TEXT, defaultValue = "${this.videoTrackDefault}"), @RestParameter(description = "The encoding profile to use", isRequired = true, name = "profileId", type = Type.STRING, defaultValue = "player-preview.http"), @RestParameter(description = "The number of seconds (many numbers can be specified, separated by semicolon) into the video to extract the image", isRequired = false, name = "time", type = Type.STRING), @RestParameter(description = "An optional set of key=value\\n properties", isRequired = false, name = "properties", type = TEXT) }, reponses = { @RestResponse(description = "Results in an xml document containing the image attachment", responseCode = HttpServletResponse.SC_OK), @RestResponse(description = "If required parameters aren't set or if sourceTrack isn't from the type Track", responseCode = HttpServletResponse.SC_BAD_REQUEST) }, returnDescription = "The image extraction job")
public Response image(@FormParam("sourceTrack") String sourceTrackXml, @FormParam("profileId") String profileId, @FormParam("time") String times, @FormParam("properties") LocalHashMap localMap) throws Exception {
    // Ensure that the POST parameters are present
    if (StringUtils.isBlank(sourceTrackXml) || StringUtils.isBlank(profileId))
        return Response.status(Response.Status.BAD_REQUEST).entity("sourceTrack and profileId must not be null").build();
    // Deserialize the source track
    MediaPackageElement sourceTrack = MediaPackageElementParser.getFromXml(sourceTrackXml);
    if (!Track.TYPE.equals(sourceTrack.getElementType()))
        return Response.status(Response.Status.BAD_REQUEST).entity("sourceTrack element must be of type track").build();
    boolean timeBased = false;
    double[] timeArray = null;
    if (StringUtils.isNotBlank(times)) {
        // parse time codes
        try {
            timeArray = parseTimeArray(times);
        } catch (Exception e) {
            return Response.status(Response.Status.BAD_REQUEST).entity("could not parse times: invalid format").build();
        }
        timeBased = true;
    } else if (localMap == null) {
        return Response.status(Response.Status.BAD_REQUEST).build();
    }
    try {
        // Asynchronously encode the specified tracks
        Job job;
        if (timeBased) {
            job = composerService.image((Track) sourceTrack, profileId, timeArray);
        } else {
            job = composerService.image((Track) sourceTrack, profileId, localMap.getMap());
        }
        return Response.ok().entity(new JaxbJob(job)).build();
    } catch (EncoderException e) {
        logger.warn("Unable to extract image(s): " + e.getMessage());
        return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
    }
}
Also used : EncoderException(org.opencastproject.composer.api.EncoderException) MediaPackageElement(org.opencastproject.mediapackage.MediaPackageElement) JaxbJob(org.opencastproject.job.api.JaxbJob) JaxbJob(org.opencastproject.job.api.JaxbJob) Job(org.opencastproject.job.api.Job) NotFoundException(org.opencastproject.util.NotFoundException) EncoderException(org.opencastproject.composer.api.EncoderException) 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 30 with Track

use of org.opencastproject.mediapackage.Track 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)

Aggregations

Track (org.opencastproject.mediapackage.Track)154 Test (org.junit.Test)56 Job (org.opencastproject.job.api.Job)56 MediaPackage (org.opencastproject.mediapackage.MediaPackage)50 URI (java.net.URI)40 MediaPackageElementFlavor (org.opencastproject.mediapackage.MediaPackageElementFlavor)34 WorkflowOperationException (org.opencastproject.workflow.api.WorkflowOperationException)34 WorkflowOperationResult (org.opencastproject.workflow.api.WorkflowOperationResult)34 HashMap (java.util.HashMap)29 ArrayList (java.util.ArrayList)27 MediaPackageException (org.opencastproject.mediapackage.MediaPackageException)24 MediaPackageElement (org.opencastproject.mediapackage.MediaPackageElement)21 TrackImpl (org.opencastproject.mediapackage.track.TrackImpl)20 NotFoundException (org.opencastproject.util.NotFoundException)20 IOException (java.io.IOException)19 TrackSelector (org.opencastproject.mediapackage.selector.TrackSelector)17 Attachment (org.opencastproject.mediapackage.Attachment)16 EncodingProfile (org.opencastproject.composer.api.EncodingProfile)15 Catalog (org.opencastproject.mediapackage.Catalog)15 File (java.io.File)14