Search in sources :

Example 31 with EncodingProfile

use of org.opencastproject.composer.api.EncodingProfile in project opencast by opencast.

the class ComposerServiceTest method testImageToVideo.

/**
 * Test method for
 * {@link org.opencastproject.composer.impl.ComposerServiceImpl#imageToVideo(org.opencastproject.mediapackage.Attachment, String, Long)}
 */
@Test
public void testImageToVideo() throws Exception {
    if (!ffmpegInstalled)
        return;
    assertTrue(sourceImage.isFile());
    // Need different media files
    Workspace workspace = EasyMock.createNiceMock(Workspace.class);
    EasyMock.expect(workspace.get(EasyMock.anyObject())).andReturn(sourceImage).anyTimes();
    EasyMock.expect(workspace.putInCollection(EasyMock.anyString(), EasyMock.anyString(), EasyMock.anyObject())).andReturn(sourceImage.toURI()).anyTimes();
    composerService.setWorkspace(workspace);
    EasyMock.replay(workspace);
    EncodingProfile imageToVideoProfile = profileScanner.getProfile("image-movie.work");
    Attachment attachment = AttachmentImpl.fromURI(sourceImage.toURI());
    attachment.setIdentifier("test image");
    Job imageToVideo = composerService.imageToVideo(attachment, imageToVideoProfile.getIdentifier(), 1L);
    Track imageToVideoTrack = (Track) MediaPackageElementParser.getFromXml(imageToVideo.getPayload());
    Assert.assertNotNull(imageToVideoTrack);
    inspectedTrack.setIdentifier(imageToVideoTrack.getIdentifier());
    inspectedTrack.setMimeType(MimeType.mimeType("video", "mp4"));
    Assert.assertEquals(inspectedTrack, imageToVideoTrack);
}
Also used : EncodingProfile(org.opencastproject.composer.api.EncodingProfile) Attachment(org.opencastproject.mediapackage.Attachment) Job(org.opencastproject.job.api.Job) Track(org.opencastproject.mediapackage.Track) Workspace(org.opencastproject.workspace.api.Workspace) Test(org.junit.Test)

Example 32 with EncodingProfile

use of org.opencastproject.composer.api.EncodingProfile in project opencast by opencast.

the class PrepareAVWorkflowOperationHandler method mux.

/**
 * Merges audio and video track of the selected flavor and adds it to the media package. If there is nothing to mux, a
 * new track with the target flavor is created (pointing to the original url).
 *
 * @param src
 *          The source media package
 * @param operation
 *          the mux workflow operation
 * @return the operation result containing the updated mediapackage
 * @throws EncoderException
 *           if encoding fails
 * @throws IOException
 *           if read/write operations from and to the workspace fail
 * @throws NotFoundException
 *           if the workspace does not contain the requested element
 */
private WorkflowOperationResult mux(MediaPackage src, WorkflowOperationInstance operation) throws EncoderException, WorkflowOperationException, NotFoundException, MediaPackageException, IOException {
    MediaPackage mediaPackage = (MediaPackage) src.clone();
    // Read the configuration properties
    String sourceFlavorName = StringUtils.trimToNull(operation.getConfiguration("source-flavor"));
    String targetTrackTags = StringUtils.trimToNull(operation.getConfiguration("target-tags"));
    String targetTrackFlavorName = StringUtils.trimToNull(operation.getConfiguration("target-flavor"));
    String muxEncodingProfileName = StringUtils.trimToNull(operation.getConfiguration("mux-encoding-profile"));
    String audioVideoEncodingProfileName = StringUtils.trimToNull(operation.getConfiguration("audio-video-encoding-profile"));
    String videoOnlyEncodingProfileName = StringUtils.trimToNull(operation.getConfiguration("video-encoding-profile"));
    String audioOnlyEncodingProfileName = StringUtils.trimToNull(operation.getConfiguration("audio-encoding-profile"));
    String[] targetTags = StringUtils.split(targetTrackTags, ",");
    List<String> removeTags = new ArrayList<String>();
    List<String> addTags = new ArrayList<String>();
    List<String> overrideTags = new ArrayList<String>();
    if (targetTags != null) {
        for (String tag : targetTags) {
            if (tag.startsWith(MINUS)) {
                removeTags.add(tag);
            } else if (tag.startsWith(PLUS)) {
                addTags.add(tag);
            } else {
                overrideTags.add(tag);
            }
        }
    }
    // Make sure the source flavor is properly set
    if (sourceFlavorName == null)
        throw new IllegalStateException("Source flavor must be specified");
    MediaPackageElementFlavor sourceFlavor = MediaPackageElementFlavor.parseFlavor(sourceFlavorName);
    // Make sure the target flavor is properly set
    if (targetTrackFlavorName == null)
        throw new IllegalStateException("Target flavor must be specified");
    MediaPackageElementFlavor targetFlavor = MediaPackageElementFlavor.parseFlavor(targetTrackFlavorName);
    // Reencode when there is no need for muxing?
    boolean rewrite = true;
    if (StringUtils.trimToNull(operation.getConfiguration(OPT_REWRITE)) != null) {
        rewrite = Boolean.parseBoolean(operation.getConfiguration(OPT_REWRITE));
    }
    String audioMuxingSourceFlavors = StringUtils.trimToNull(operation.getConfiguration(OPT_AUDIO_MUXING_SOURCE_FLAVORS));
    // Select those tracks that have matching flavors
    Track[] tracks = mediaPackage.getTracks(sourceFlavor);
    Track audioTrack = null;
    Track videoTrack = null;
    switch(tracks.length) {
        case 0:
            logger.info("No audio/video tracks with flavor '{}' found to prepare", sourceFlavor);
            return createResult(mediaPackage, Action.CONTINUE);
        case 1:
            videoTrack = tracks[0];
            if (!tracks[0].hasAudio() && tracks[0].hasVideo() && (audioMuxingSourceFlavors != null)) {
                audioTrack = findAudioTrack(tracks[0], mediaPackage, audioMuxingSourceFlavors);
            } else {
                audioTrack = tracks[0];
            }
            break;
        case 2:
            for (Track track : tracks) {
                if (track.hasAudio() && !track.hasVideo()) {
                    audioTrack = track;
                } else if (!track.hasAudio() && track.hasVideo()) {
                    videoTrack = track;
                } else {
                    throw new WorkflowOperationException("Multiple tracks with competing audio/video streams and flavor '" + sourceFlavor + "' found");
                }
            }
            break;
        default:
            logger.error("More than two tracks with flavor {} found. No idea what we should be doing", sourceFlavor);
            throw new WorkflowOperationException("More than two tracks with flavor '" + sourceFlavor + "' found");
    }
    Job job = null;
    Track composedTrack = null;
    // Make sure we have a matching combination
    if (audioTrack == null && videoTrack != null) {
        if (rewrite) {
            logger.info("Encoding video only track {} to work version", videoTrack);
            if (videoOnlyEncodingProfileName == null)
                videoOnlyEncodingProfileName = PREPARE_VONLY_PROFILE;
            // Find the encoding profile to make sure the given profile exists
            EncodingProfile profile = composerService.getProfile(videoOnlyEncodingProfileName);
            if (profile == null)
                throw new IllegalStateException("Encoding profile '" + videoOnlyEncodingProfileName + "' was not found");
            composedTrack = prepare(videoTrack, mediaPackage, videoOnlyEncodingProfileName);
        } else {
            composedTrack = (Track) videoTrack.clone();
            composedTrack.setIdentifier(null);
            mediaPackage.add(composedTrack);
        }
    } else if (videoTrack == null && audioTrack != null) {
        if (rewrite) {
            logger.info("Encoding audio only track {} to work version", audioTrack);
            if (audioOnlyEncodingProfileName == null)
                audioOnlyEncodingProfileName = PREPARE_AONLY_PROFILE;
            // Find the encoding profile to make sure the given profile exists
            EncodingProfile profile = composerService.getProfile(audioOnlyEncodingProfileName);
            if (profile == null)
                throw new IllegalStateException("Encoding profile '" + audioOnlyEncodingProfileName + "' was not found");
            composedTrack = prepare(audioTrack, mediaPackage, audioOnlyEncodingProfileName);
        } else {
            composedTrack = (Track) audioTrack.clone();
            composedTrack.setIdentifier(null);
            mediaPackage.add(composedTrack);
        }
    } else if (audioTrack == videoTrack) {
        if (rewrite) {
            logger.info("Encoding audiovisual track {} to work version", videoTrack);
            if (audioVideoEncodingProfileName == null)
                audioVideoEncodingProfileName = PREPARE_AV_PROFILE;
            // Find the encoding profile to make sure the given profile exists
            EncodingProfile profile = composerService.getProfile(audioVideoEncodingProfileName);
            if (profile == null)
                throw new IllegalStateException("Encoding profile '" + audioVideoEncodingProfileName + "' was not found");
            composedTrack = prepare(videoTrack, mediaPackage, audioVideoEncodingProfileName);
        } else {
            composedTrack = (Track) videoTrack.clone();
            composedTrack.setIdentifier(null);
            mediaPackage.add(composedTrack);
        }
    } else {
        logger.info("Muxing audio and video only track {} to work version", videoTrack);
        if (audioTrack.hasVideo()) {
            logger.info("Stripping audio from track {}", audioTrack);
            audioTrack = prepare(audioTrack, null, PREPARE_AONLY_PROFILE);
        }
        if (muxEncodingProfileName == null)
            muxEncodingProfileName = MUX_AV_PROFILE;
        // Find the encoding profile
        EncodingProfile profile = composerService.getProfile(muxEncodingProfileName);
        if (profile == null)
            throw new IllegalStateException("Encoding profile '" + muxEncodingProfileName + "' was not found");
        job = composerService.mux(videoTrack, audioTrack, profile.getIdentifier());
        if (!waitForStatus(job).isSuccess()) {
            throw new WorkflowOperationException("Muxing video track " + videoTrack + " and audio track " + audioTrack + " failed");
        }
        composedTrack = (Track) MediaPackageElementParser.getFromXml(job.getPayload());
        mediaPackage.add(composedTrack);
        String fileName = getFileNameFromElements(videoTrack, composedTrack);
        composedTrack.setURI(workspace.moveTo(composedTrack.getURI(), mediaPackage.getIdentifier().toString(), composedTrack.getIdentifier(), fileName));
    }
    long timeInQueue = 0;
    if (job != null) {
        // add this receipt's queue time to the total
        timeInQueue = job.getQueueTime();
    }
    // Update the track's flavor
    composedTrack.setFlavor(targetFlavor);
    logger.debug("Composed track has flavor '{}'", composedTrack.getFlavor());
    // Add the target tags
    if (overrideTags.size() > 0) {
        composedTrack.clearTags();
        for (String tag : overrideTags) {
            logger.trace("Tagging composed track with '{}'", tag);
            composedTrack.addTag(tag);
        }
    } else {
        for (String tag : removeTags) {
            logger.trace("Remove tagging '{}' from composed track", tag);
            composedTrack.removeTag(tag.substring(MINUS.length()));
        }
        for (String tag : addTags) {
            logger.trace("Add tagging '{}' to composed track", tag);
            composedTrack.addTag(tag.substring(PLUS.length()));
        }
    }
    return createResult(mediaPackage, Action.CONTINUE, timeInQueue);
}
Also used : ArrayList(java.util.ArrayList) EncodingProfile(org.opencastproject.composer.api.EncodingProfile) MediaPackageElementFlavor(org.opencastproject.mediapackage.MediaPackageElementFlavor) MediaPackage(org.opencastproject.mediapackage.MediaPackage) WorkflowOperationException(org.opencastproject.workflow.api.WorkflowOperationException) Job(org.opencastproject.job.api.Job) Track(org.opencastproject.mediapackage.Track)

Example 33 with EncodingProfile

use of org.opencastproject.composer.api.EncodingProfile in project opencast by opencast.

the class ComposeWorkflowOperationHandler method encode.

/**
 * Encode tracks from MediaPackage using profiles stored in properties and updates current MediaPackage.
 *
 * @param src
 *          The source media package
 * @param operation
 *          the current workflow operation
 * @return the operation result containing the updated media package
 * @throws EncoderException
 *           if encoding fails
 * @throws WorkflowOperationException
 *           if errors occur during processing
 * @throws IOException
 *           if the workspace operations fail
 * @throws NotFoundException
 *           if the workspace doesn't contain the requested file
 */
private WorkflowOperationResult encode(MediaPackage src, WorkflowOperationInstance operation) throws EncoderException, IOException, NotFoundException, MediaPackageException, WorkflowOperationException {
    MediaPackage mediaPackage = (MediaPackage) src.clone();
    // Check which tags have been configured
    String sourceTagsOption = StringUtils.trimToNull(operation.getConfiguration("source-tags"));
    String targetTagsOption = StringUtils.trimToNull(operation.getConfiguration("target-tags"));
    String sourceFlavorOption = StringUtils.trimToNull(operation.getConfiguration("source-flavor"));
    String sourceFlavorsOption = StringUtils.trimToNull(operation.getConfiguration("source-flavors"));
    String targetFlavorOption = StringUtils.trimToNull(operation.getConfiguration("target-flavor"));
    boolean tagsAndFlavorsOption = Boolean.parseBoolean(StringUtils.trimToNull(operation.getConfiguration("tags-and-flavors")));
    AbstractMediaPackageElementSelector<Track> elementSelector = new TrackSelector();
    // Make sure either one of tags or flavors are provided
    if (StringUtils.isBlank(sourceTagsOption) && StringUtils.isBlank(sourceFlavorOption) && StringUtils.isBlank(sourceFlavorsOption)) {
        logger.info("No source tags or flavors have been specified, not matching anything");
        return createResult(mediaPackage, Action.CONTINUE);
    }
    // Select the source flavors
    for (String flavor : asList(sourceFlavorsOption)) {
        try {
            elementSelector.addFlavor(MediaPackageElementFlavor.parseFlavor(flavor));
        } catch (IllegalArgumentException e) {
            throw new WorkflowOperationException("Source flavor '" + flavor + "' is malformed");
        }
    }
    // Support legacy "source-flavor" option
    if (StringUtils.isNotBlank(sourceFlavorOption)) {
        String flavor = StringUtils.trim(sourceFlavorOption);
        try {
            elementSelector.addFlavor(MediaPackageElementFlavor.parseFlavor(flavor));
        } catch (IllegalArgumentException e) {
            throw new WorkflowOperationException("Source flavor '" + flavor + "' is malformed");
        }
    }
    // Select the source tags
    for (String tag : asList(sourceTagsOption)) {
        elementSelector.addTag(tag);
    }
    // Find the encoding profile
    String profilesOption = StringUtils.trimToNull(operation.getConfiguration("encoding-profiles"));
    List<EncodingProfile> profiles = new ArrayList<EncodingProfile>();
    for (String profileName : asList(profilesOption)) {
        EncodingProfile profile = composerService.getProfile(profileName);
        if (profile == null)
            throw new WorkflowOperationException("Encoding profile '" + profileName + "' was not found");
        profiles.add(profile);
    }
    // Support legacy "encoding-profile" option
    String profileOption = StringUtils.trimToNull(operation.getConfiguration("encoding-profile"));
    if (StringUtils.isNotBlank(profileOption)) {
        String profileId = StringUtils.trim(profileOption);
        EncodingProfile profile = composerService.getProfile(profileId);
        if (profile == null)
            throw new WorkflowOperationException("Encoding profile '" + profileId + "' was not found");
        profiles.add(profile);
    }
    // Make sure there is at least one profile
    if (profiles.isEmpty())
        throw new WorkflowOperationException("No encoding profile was specified");
    // Audio / Video only?
    String audioOnlyConfig = StringUtils.trimToNull(operation.getConfiguration("audio-only"));
    String videoOnlyConfig = StringUtils.trimToNull(operation.getConfiguration("video-only"));
    boolean audioOnly = audioOnlyConfig != null && Boolean.parseBoolean(audioOnlyConfig);
    boolean videoOnly = videoOnlyConfig != null && Boolean.parseBoolean(videoOnlyConfig);
    // Target tags
    List<String> targetTags = asList(targetTagsOption);
    // Target flavor
    MediaPackageElementFlavor targetFlavor = null;
    if (StringUtils.isNotBlank(targetFlavorOption)) {
        try {
            targetFlavor = MediaPackageElementFlavor.parseFlavor(targetFlavorOption);
        } catch (IllegalArgumentException e) {
            throw new WorkflowOperationException("Target flavor '" + targetFlavorOption + "' is malformed");
        }
    }
    // Look for elements matching the tag
    Collection<Track> elements = elementSelector.select(mediaPackage, tagsAndFlavorsOption);
    String processOnlyOneConfig = StringUtils.trimToNull(operation.getConfiguration("process-first-match-only"));
    boolean processOnlyOne = processOnlyOneConfig != null && Boolean.parseBoolean(processOnlyOneConfig);
    // Encode all tracks found
    long totalTimeInQueue = 0;
    Map<Job, JobInformation> encodingJobs = new HashMap<Job, JobInformation>();
    for (Track track : elements) {
        // Skip audio/video only mismatches
        if (audioOnly && track.hasVideo()) {
            logger.info("Skipping encoding of '{}', since it contains a video stream", track);
            continue;
        } else if (videoOnly && track.hasAudio()) {
            logger.info("Skipping encoding of '{}', since it containsa an audio stream", track);
            continue;
        }
        // Encode the track with all profiles
        for (EncodingProfile profile : profiles) {
            // Check if the track supports the output type of the profile
            MediaType outputType = profile.getOutputType();
            if (outputType.equals(MediaType.Audio) && !track.hasAudio()) {
                logger.info("Skipping encoding of '{}', since it lacks an audio stream", track);
                continue;
            } else if (outputType.equals(MediaType.Visual) && !track.hasVideo()) {
                logger.info("Skipping encoding of '{}', since it lacks a video stream", track);
                continue;
            }
            logger.info("Encoding track {} using encoding profile '{}'", track, profile);
            // Start encoding and wait for the result
            encodingJobs.put(composerService.encode(track, profile.getIdentifier()), new JobInformation(track, profile));
            if (processOnlyOne)
                break;
        }
    }
    if (encodingJobs.isEmpty()) {
        logger.info("No matching tracks found");
        return createResult(mediaPackage, Action.CONTINUE);
    }
    // Wait for the jobs to return
    if (!waitForStatus(encodingJobs.keySet().toArray(new Job[encodingJobs.size()])).isSuccess()) {
        throw new WorkflowOperationException("One of the encoding jobs did not complete successfully");
    }
    // Process the result
    for (Map.Entry<Job, JobInformation> entry : encodingJobs.entrySet()) {
        Job job = entry.getKey();
        Track track = entry.getValue().getTrack();
        // add this receipt's queue time to the total
        totalTimeInQueue += job.getQueueTime();
        // it is allowed for compose jobs to return an empty payload. See the EncodeEngine interface
        if (job.getPayload().length() > 0) {
            Track composedTrack = (Track) MediaPackageElementParser.getFromXml(job.getPayload());
            // Adjust the target tags
            for (String tag : targetTags) {
                logger.trace("Tagging composed track with '{}'", tag);
                composedTrack.addTag(tag);
            }
            // Adjust the target flavor. Make sure to account for partial updates
            if (targetFlavor != null) {
                String flavorType = targetFlavor.getType();
                String flavorSubtype = targetFlavor.getSubtype();
                if ("*".equals(flavorType))
                    flavorType = track.getFlavor().getType();
                if ("*".equals(flavorSubtype))
                    flavorSubtype = track.getFlavor().getSubtype();
                composedTrack.setFlavor(new MediaPackageElementFlavor(flavorType, flavorSubtype));
                logger.debug("Composed track has flavor '{}'", composedTrack.getFlavor());
            }
            // store new tracks to mediaPackage
            mediaPackage.addDerived(composedTrack, track);
            String fileName = getFileNameFromElements(track, composedTrack);
            composedTrack.setURI(workspace.moveTo(composedTrack.getURI(), mediaPackage.getIdentifier().toString(), composedTrack.getIdentifier(), fileName));
        }
    }
    WorkflowOperationResult result = createResult(mediaPackage, Action.CONTINUE, totalTimeInQueue);
    logger.debug("Compose operation completed");
    return result;
}
Also used : HashMap(java.util.HashMap) TrackSelector(org.opencastproject.mediapackage.selector.TrackSelector) ArrayList(java.util.ArrayList) EncodingProfile(org.opencastproject.composer.api.EncodingProfile) MediaPackageElementFlavor(org.opencastproject.mediapackage.MediaPackageElementFlavor) WorkflowOperationResult(org.opencastproject.workflow.api.WorkflowOperationResult) MediaPackage(org.opencastproject.mediapackage.MediaPackage) WorkflowOperationException(org.opencastproject.workflow.api.WorkflowOperationException) MediaType(org.opencastproject.composer.api.EncodingProfile.MediaType) Job(org.opencastproject.job.api.Job) HashMap(java.util.HashMap) Map(java.util.Map) TreeMap(java.util.TreeMap) SortedMap(java.util.SortedMap) Track(org.opencastproject.mediapackage.Track)

Example 34 with EncodingProfile

use of org.opencastproject.composer.api.EncodingProfile in project opencast by opencast.

the class EncodingProfileTest method testGetExtension.

/**
 * Test method for {@link org.opencastproject.composer.api.EncodingProfileImpl#getExtension(java.lang.String)}.
 */
@Test
public void testGetExtension() {
    EncodingProfile profile = profiles.get(h264ProfileId);
    assertNull(profile.getExtension("test"));
    // Test profile with existing extension
    profile = profiles.get(coverProfileId);
    String commandline = "-i #{in.path} -y -r 1 -t 1 -f image2 -s 160x120 #{out.dir}/#{in.name}#{out.suffix}";
    assertEquals(commandline, profile.getExtension("ffmpeg.command"));
}
Also used : EncodingProfile(org.opencastproject.composer.api.EncodingProfile) Test(org.junit.Test)

Example 35 with EncodingProfile

use of org.opencastproject.composer.api.EncodingProfile in project opencast by opencast.

the class EncodingProfileTest method testGetIdentifier.

/**
 * Test method for {@link org.opencastproject.composer.api.EncodingProfileImpl#getIdentifier()}.
 */
@Test
public void testGetIdentifier() {
    EncodingProfile profile = profiles.get(h264ProfileId);
    assertEquals(h264ProfileId, profile.getIdentifier());
}
Also used : EncodingProfile(org.opencastproject.composer.api.EncodingProfile) Test(org.junit.Test)

Aggregations

EncodingProfile (org.opencastproject.composer.api.EncodingProfile)38 EncoderException (org.opencastproject.composer.api.EncoderException)16 Track (org.opencastproject.mediapackage.Track)15 HashMap (java.util.HashMap)14 Job (org.opencastproject.job.api.Job)13 ArrayList (java.util.ArrayList)12 Test (org.junit.Test)12 Map (java.util.Map)10 ServiceRegistryException (org.opencastproject.serviceregistry.api.ServiceRegistryException)10 File (java.io.File)9 URI (java.net.URI)8 IOException (java.io.IOException)7 WorkflowOperationException (org.opencastproject.workflow.api.WorkflowOperationException)7 Attachment (org.opencastproject.mediapackage.Attachment)6 MediaPackage (org.opencastproject.mediapackage.MediaPackage)6 MediaPackageElementFlavor (org.opencastproject.mediapackage.MediaPackageElementFlavor)6 MediaPackageException (org.opencastproject.mediapackage.MediaPackageException)6 NotFoundException (org.opencastproject.util.NotFoundException)6 LinkedList (java.util.LinkedList)5 WorkflowOperationResult (org.opencastproject.workflow.api.WorkflowOperationResult)5