/** * Licensed to The Apereo Foundation under one or more contributor license * agreements. See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * * * The Apereo Foundation licenses this file to you under the Educational * Community License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of the License * at: * * http://opensource.org/licenses/ecl2.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. * */ package org.opencastproject.workflow.handler.composer; import org.opencastproject.composer.api.ComposerService; import org.opencastproject.composer.api.EncoderException; import org.opencastproject.composer.api.EncodingProfile; import org.opencastproject.composer.api.EncodingProfile.MediaType; import org.opencastproject.composer.layout.Dimension; import org.opencastproject.job.api.Job; import org.opencastproject.mediapackage.MediaPackage; import org.opencastproject.mediapackage.MediaPackageBuilder; import org.opencastproject.mediapackage.MediaPackageBuilderFactory; import org.opencastproject.mediapackage.MediaPackageElementFlavor; import org.opencastproject.mediapackage.MediaPackageElementParser; import org.opencastproject.mediapackage.MediaPackageException; import org.opencastproject.mediapackage.Track; import org.opencastproject.mediapackage.TrackSupport; import org.opencastproject.mediapackage.VideoStream; import org.opencastproject.serviceregistry.api.ServiceRegistry; import org.opencastproject.util.MimeTypes; import org.opencastproject.workflow.api.WorkflowInstance.WorkflowState; import org.opencastproject.workflow.api.WorkflowInstanceImpl; import org.opencastproject.workflow.api.WorkflowOperationException; import org.opencastproject.workflow.api.WorkflowOperationInstance; import org.opencastproject.workflow.api.WorkflowOperationInstance.OperationState; import org.opencastproject.workflow.api.WorkflowOperationInstanceImpl; import org.opencastproject.workflow.api.WorkflowOperationResult; import org.opencastproject.workflow.api.WorkflowOperationResult.Action; import org.opencastproject.workflow.handler.inspection.InspectWorkflowOperationHandler; import org.opencastproject.workspace.api.Workspace; import org.apache.commons.lang3.StringUtils; import org.easymock.EasyMock; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import java.net.URI; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; public class ConcatWorkflowOperationHandlerTest { private ConcatWorkflowOperationHandler operationHandler; // local resources private MediaPackage mp; private MediaPackage mpEncode; private Job job; private Track[] encodedTracks; // mock services and objects private EncodingProfile profile = null; private ComposerService composerService = null; private Workspace workspace = null; // constant metadata values private static final String PROFILE_ID = "concat"; private static final String ENCODED_TRACK_ID = "concatenated-workflow-operation-test-encode-track-id"; @Before public void setUp() throws Exception { MediaPackageBuilder builder = MediaPackageBuilderFactory.newInstance().newMediaPackageBuilder(); // test resources URI uriMP = InspectWorkflowOperationHandler.class.getResource("/concat_mediapackage.xml").toURI(); URI uriMPEncode = InspectWorkflowOperationHandler.class.getResource("/concatenated_mediapackage.xml").toURI(); mp = builder.loadFromXml(uriMP.toURL().openStream()); mpEncode = builder.loadFromXml(uriMPEncode.toURL().openStream()); encodedTracks = mpEncode.getTracks(); // set up mock workspace workspace = EasyMock.createNiceMock(Workspace.class); EasyMock.expect( workspace.moveTo((URI) EasyMock.anyObject(), (String) EasyMock.anyObject(), (String) EasyMock.anyObject(), (String) EasyMock.anyObject())).andReturn(uriMP); EasyMock.replay(workspace); // set up mock receipt job = EasyMock.createNiceMock(Job.class); EasyMock.expect(job.getPayload()).andReturn(MediaPackageElementParser.getAsXml(encodedTracks[0])).anyTimes(); EasyMock.expect(job.getStatus()).andReturn(Job.Status.FINISHED); EasyMock.expect(job.getDateCreated()).andReturn(new Date()); EasyMock.expect(job.getDateStarted()).andReturn(new Date()); EasyMock.expect(job.getQueueTime()).andReturn(new Long(0)); EasyMock.replay(job); // set up mock service registry ServiceRegistry serviceRegistry = EasyMock.createNiceMock(ServiceRegistry.class); EasyMock.expect(serviceRegistry.getJob(EasyMock.anyLong())).andReturn(job); EasyMock.replay(serviceRegistry); // set up service operationHandler = new ConcatWorkflowOperationHandler(); operationHandler.setJobBarrierPollingInterval(0); operationHandler.setWorkspace(workspace); operationHandler.setServiceRegistry(serviceRegistry); } @Test public void testConcat2EncodedTracksWithFlavor() throws Exception { setMockups(); // operation configuration String targetTags = "engage,rss"; Map<String, String> configurations = new HashMap<String, String>(); configurations.put("source-flavor-part-0", "presentation/source"); configurations.put("source-flavor-part-1", "presenter/source"); configurations.put("target-tags", targetTags); configurations.put("target-flavor", "presenter/concat"); configurations.put("encoding-profile", "concat"); configurations.put("output-resolution", "1900x1080"); // run the operation handler WorkflowOperationResult result = getWorkflowOperationResult(mp, configurations); // check track metadata MediaPackage mpNew = result.getMediaPackage(); Track trackEncoded = mpNew.getTrack(ENCODED_TRACK_ID); Assert.assertEquals("presenter/concat", trackEncoded.getFlavor().toString()); Assert.assertArrayEquals(targetTags.split("\\W"), trackEncoded.getTags()); } @Test public void testResolutionByTrackMandatory() throws Exception { setMockups(); // operation configuration String targetTags = "engage,rss"; Map<String, String> configurations = new HashMap<String, String>(); configurations.put("source-flavor-part-0", "presentation/source"); configurations.put("source-flavor-part-1", "presenter/source"); configurations.put("source-flavor-part-1-mandatory", "true"); configurations.put("target-tags", targetTags); configurations.put("target-flavor", "presenter/concat"); configurations.put("encoding-profile", "concat"); configurations.put("output-resolution", "part-1"); // run the operation handler WorkflowOperationResult result = getWorkflowOperationResult(mp, configurations); // check track metadata MediaPackage mpNew = result.getMediaPackage(); Track trackEncoded = mpNew.getTrack(ENCODED_TRACK_ID); Assert.assertEquals("presenter/concat", trackEncoded.getFlavor().toString()); Assert.assertArrayEquals(targetTags.split("\\W"), trackEncoded.getTags()); } @Test public void testResolutionByTrackNotMandatory() throws Exception { setMockups(); // operation configuration String targetTags = "engage,rss"; Map<String, String> configurations = new HashMap<String, String>(); configurations.put("source-flavor-part-0", "presentation/source"); configurations.put("source-flavor-part-1", "presenter/source"); configurations.put("source-flavor-part-1-mandatory", "false"); configurations.put("target-tags", targetTags); configurations.put("target-flavor", "presenter/concat"); configurations.put("encoding-profile", "concat"); configurations.put("output-resolution", "part-1"); // run the operation handler WorkflowOperationResult result = getWorkflowOperationResult(mp, configurations); Assert.assertEquals(Action.SKIP, result.getAction()); } @Test public void testFrameRateFixedValue() throws Exception { createTestFrameRateWithValue("25", 25.0f); } @Test public void testFrameRateFixedDecimalValue() throws Exception { createTestFrameRateWithValue("25.000", 25.0f); } @Test public void testFrameRatePartValue() throws Exception { Track part1 = (Track) mp.getElementsByFlavor(MediaPackageElementFlavor.parseFlavor("presenter/source"))[0]; VideoStream[] videoStreams = TrackSupport.byType(part1.getStreams(), VideoStream.class); createTestFrameRateWithValue("part-1", videoStreams[0].getFrameRate()); } @Test(expected = WorkflowOperationException.class) public void testFrameRateInvalidDigitPartValue() throws Exception { createTestFrameRateWithValue("part-10", Float.MAX_VALUE); } @Test(expected = WorkflowOperationException.class) public void testFrameRateInvalidPartValue() throws Exception { createTestFrameRateWithValue("part-foo", Float.MAX_VALUE); } @Test(expected = WorkflowOperationException.class) public void testFrameRateInvalidValue() throws Exception { createTestFrameRateWithValue("foo", Float.MAX_VALUE); } protected void createTestFrameRateWithValue(String frameRateValue, float expectedFrameRateValue) throws Exception { setMockupsWithFrameRate(expectedFrameRateValue); // operation configuration String targetTags = "engage,rss"; Map<String, String> configurations = new HashMap<String, String>(); configurations.put("source-flavor-part-0", "presentation/source"); configurations.put("source-flavor-part-1", "presenter/source"); configurations.put("source-flavor-part-1-mandatory", "true"); configurations.put("target-tags", targetTags); configurations.put("target-flavor", "presenter/concat"); configurations.put("encoding-profile", "concat"); configurations.put("output-resolution", "part-1"); configurations.put("output-framerate", frameRateValue); // run the operation handler WorkflowOperationResult result = getWorkflowOperationResult(mp, configurations); // check track metadata MediaPackage mpNew = result.getMediaPackage(); Track trackEncoded = mpNew.getTrack(ENCODED_TRACK_ID); Assert.assertEquals("presenter/concat", trackEncoded.getFlavor().toString()); Assert.assertArrayEquals(targetTags.split("\\W"), trackEncoded.getTags()); } @Test public void testConcat2EncodedTracksWithTags() throws Exception { setMockups(); // operation configuration String targetTags = "engage,rss"; Map<String, String> configurations = new HashMap<String, String>(); configurations.put("source-flavor-part-0", "presentation/source"); configurations.put("source-flavor-part-1", "presenter/source"); configurations.put("source-tags-part-0", "part0,part0b"); configurations.put("source-tags-part-1", "part1"); configurations.put("target-tags", targetTags); configurations.put("target-flavor", "presenter/concat"); configurations.put("encoding-profile", "concat"); configurations.put("output-resolution", "1900x1080"); // run the operation handler WorkflowOperationResult result = getWorkflowOperationResult(mp, configurations); // check track metadata MediaPackage mpNew = result.getMediaPackage(); Track trackEncoded = mpNew.getTrack(ENCODED_TRACK_ID); Assert.assertEquals("presenter/concat", trackEncoded.getFlavor().toString()); Assert.assertArrayEquals(targetTags.split("\\W"), trackEncoded.getTags()); } @Test public void testConcatMandatoryCheck() throws Exception { setMockups(); // operation configuration String targetTags = "engage,rss"; Map<String, String> configurations = new HashMap<String, String>(); configurations.put("source-flavor-part-0", "presentation/source"); configurations.put("source-flavor-part-1", "test/source"); configurations.put("target-tags", targetTags); configurations.put("target-flavor", "presenter/concat"); configurations.put("encoding-profile", "concat"); configurations.put("output-resolution", "1900x1080"); // run the operation handler WorkflowOperationResult result = getWorkflowOperationResult(mp, configurations); Assert.assertEquals(Action.SKIP, result.getAction()); } @Test public void testConcatOptionalCheck() throws Exception { setMockups(); // operation configuration String targetTags = "engage,rss"; Map<String, String> configurations = new HashMap<String, String>(); configurations.put("source-flavor-part-0", "presentation/source"); configurations.put("source-flavor-part-1", "test/source"); configurations.put("source-flavor-part-1-mandatory", "false"); configurations.put("source-flavor-part-2", "presentation/source"); configurations.put("target-tags", targetTags); configurations.put("target-flavor", "presenter/concat"); configurations.put("encoding-profile", "concat"); configurations.put("output-resolution", "1900x1080"); // run the operation handler WorkflowOperationResult result = getWorkflowOperationResult(mp, configurations); Assert.assertEquals(Action.CONTINUE, result.getAction()); } @Test public void testConcatLessTracks() throws Exception { setMockups(); // operation configuration String targetTags = "engage,rss"; Map<String, String> configurations = new HashMap<String, String>(); configurations.put("source-flavor-part-0", "presentation/source"); configurations.put("target-tags", targetTags); configurations.put("target-flavor", "presenter/concat"); configurations.put("encoding-profile", "concat"); configurations.put("output-resolution", "1900x1080"); // run the operation handler WorkflowOperationResult result = getWorkflowOperationResult(mp, configurations); Assert.assertEquals(Action.SKIP, result.getAction()); // check track metadata MediaPackage mpNew = result.getMediaPackage(); Track[] tracks = mpNew.getTracks(MediaPackageElementFlavor.parseFlavor("presenter/concat")); Assert.assertEquals(1, tracks.length); Assert.assertArrayEquals(StringUtils.split(targetTags, ","), tracks[0].getTags()); } private void setMockups() throws EncoderException, MediaPackageException { setMockupsWithFrameRate(-1.0f); } private void setMockupsWithFrameRate(float expectedFramerate) throws EncoderException, MediaPackageException { // set up mock profile profile = EasyMock.createNiceMock(EncodingProfile.class); EasyMock.expect(profile.getIdentifier()).andReturn(PROFILE_ID); EasyMock.expect(profile.getApplicableMediaType()).andReturn(MediaType.Stream); EasyMock.expect(profile.getOutputType()).andReturn(MediaType.AudioVisual); EasyMock.expect(profile.getMimeType()).andReturn(MimeTypes.MPEG4.toString()).times(2); EasyMock.replay(profile); // set up mock composer service composerService = EasyMock.createNiceMock(ComposerService.class); EasyMock.expect(composerService.getProfile(PROFILE_ID)).andReturn(profile); if (expectedFramerate > 0) { EasyMock.expect(composerService.concat( (String) EasyMock.anyObject(), (Dimension) EasyMock.anyObject(), EasyMock.eq(expectedFramerate), (Track) EasyMock.anyObject(), (Track) EasyMock.anyObject())).andReturn(job); } else { EasyMock.expect(composerService.concat( (String) EasyMock.anyObject(), (Dimension) EasyMock.anyObject(), (Track) EasyMock.anyObject(), (Track) EasyMock.anyObject())).andReturn(job); } EasyMock.replay(composerService); operationHandler.setComposerService(composerService); } private WorkflowOperationResult getWorkflowOperationResult(MediaPackage mp, Map<String, String> configurations) throws WorkflowOperationException { // Add the mediapackage to a workflow instance WorkflowInstanceImpl workflowInstance = new WorkflowInstanceImpl(); workflowInstance.setId(1); workflowInstance.setState(WorkflowState.RUNNING); workflowInstance.setMediaPackage(mp); WorkflowOperationInstanceImpl operation = new WorkflowOperationInstanceImpl("op", OperationState.RUNNING); operation.setTemplate("concat"); operation.setState(OperationState.RUNNING); for (String key : configurations.keySet()) { operation.setConfiguration(key, configurations.get(key)); } List<WorkflowOperationInstance> operationsList = new ArrayList<WorkflowOperationInstance>(); operationsList.add(operation); workflowInstance.setOperations(operationsList); // Run the media package through the operation handler, ensuring that metadata gets added return operationHandler.start(workflowInstance, null); } }