1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.opencastproject.workflow.handler.crop;
22
23 import org.opencastproject.crop.api.CropException;
24 import org.opencastproject.crop.api.CropService;
25 import org.opencastproject.job.api.Job;
26 import org.opencastproject.job.api.JobContext;
27 import org.opencastproject.mediapackage.MediaPackage;
28 import org.opencastproject.mediapackage.MediaPackageElementFlavor;
29 import org.opencastproject.mediapackage.MediaPackageElementParser;
30 import org.opencastproject.mediapackage.MediaPackageException;
31 import org.opencastproject.mediapackage.Track;
32 import org.opencastproject.serviceregistry.api.ServiceRegistry;
33 import org.opencastproject.util.NotFoundException;
34 import org.opencastproject.workflow.api.AbstractWorkflowOperationHandler;
35 import org.opencastproject.workflow.api.ConfiguredTagsAndFlavors;
36 import org.opencastproject.workflow.api.WorkflowInstance;
37 import org.opencastproject.workflow.api.WorkflowOperationException;
38 import org.opencastproject.workflow.api.WorkflowOperationHandler;
39 import org.opencastproject.workflow.api.WorkflowOperationInstance;
40 import org.opencastproject.workflow.api.WorkflowOperationResult;
41 import org.opencastproject.workspace.api.Workspace;
42
43 import org.osgi.service.component.annotations.Component;
44 import org.osgi.service.component.annotations.Reference;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47
48 import java.io.IOException;
49 import java.util.ArrayList;
50 import java.util.Arrays;
51 import java.util.HashMap;
52 import java.util.List;
53 import java.util.Map;
54
55
56
57
58 @Component(
59 immediate = true,
60 service = WorkflowOperationHandler.class,
61 property = {
62 "service.description=Videosegmentation Workflow Operation Handler",
63 "workflow.operation=crop-video"
64 }
65 )
66 public class CropWorkflowOperationHandler extends AbstractWorkflowOperationHandler {
67
68
69 private static final Logger logger = LoggerFactory.getLogger(CropWorkflowOperationHandler.class);
70
71
72 private static final String PROP_SOURCE_FLAVOR = "source-flavor";
73
74 private static final String PROP_TARGET_FLAVOR = "target-flavor";
75
76
77 private static final String PROP_TARGET_TAGS = "target-tags";
78
79
80 private CropService cropService = null;
81
82
83 private Workspace workspace = null;
84
85
86
87
88
89
90
91 @Override
92 public WorkflowOperationResult start(WorkflowInstance workflowInstance, JobContext jobContext)
93 throws WorkflowOperationException {
94
95 WorkflowOperationInstance operation = workflowInstance.getCurrentOperation();
96 MediaPackage mediaPackage = workflowInstance.getMediaPackage();
97
98 logger.info("Start cropping workflow operation for mediapackage {}", mediaPackage.getIdentifier().toString());
99
100
101 ConfiguredTagsAndFlavors tagsAndFlavors = getTagsAndFlavors(workflowInstance,
102 Configuration.none, Configuration.one, Configuration.many, Configuration.many);
103 ConfiguredTagsAndFlavors.TargetTags targetTags = tagsAndFlavors.getTargetTags();
104 List<MediaPackageElementFlavor> targetFlavorOption = tagsAndFlavors.getTargetFlavors();
105
106 MediaPackageElementFlavor targetFlavor = null;
107 if (!targetFlavorOption.isEmpty()) {
108 targetFlavor = targetFlavorOption.get(0);
109 }
110 MediaPackageElementFlavor trackFlavor = tagsAndFlavors.getSingleSrcFlavor();
111
112 List<Track> candidates = new ArrayList<>();
113 candidates.addAll(Arrays.asList(mediaPackage.getTracks(trackFlavor)));
114 candidates.removeIf(t -> !t.hasVideo());
115
116 if (candidates.size() == 0) {
117 logger.info("No matching tracks available for cropping in workflow {}", workflowInstance);
118 return createResult(WorkflowOperationResult.Action.CONTINUE);
119 } else if (candidates.size() > 1) {
120 logger.info("Found more than one track to crop");
121 }
122
123
124 Map<Job, Track> jobs = new HashMap<Job, Track>();
125 for (Track candidate : candidates) {
126 try {
127 jobs.put(cropService.crop(candidate), candidate);
128 } catch (MediaPackageException | CropException e) {
129 throw new WorkflowOperationException("Failed starting crop job", e);
130 }
131 }
132
133
134 if (!waitForStatus(jobs.keySet().toArray(new Job[0])).isSuccess()) {
135 throw new WorkflowOperationException("Crop operation failed");
136 }
137
138 long totalTimeInQueue = 0;
139
140
141 for (Map.Entry<Job, Track> entry : jobs.entrySet()) {
142 Job job = entry.getKey();
143 Track track = entry.getValue();
144
145 Track croppedTrack;
146 try {
147 croppedTrack = (Track) MediaPackageElementParser.getFromXml(job.getPayload());
148 } catch (MediaPackageException e) {
149 throw new WorkflowOperationException(String.format("Crop service yielded invalid track: %s", job.getPayload()));
150 }
151
152
153 croppedTrack.generateIdentifier();
154
155
156 try {
157 String filename = "cropped_" + croppedTrack.getURI().toString();
158 croppedTrack.setURI(workspace
159 .moveTo(croppedTrack.getURI(), mediaPackage.getIdentifier().toString(), croppedTrack.getIdentifier(),
160 filename));
161 } catch (NotFoundException | IOException e) {
162 throw new WorkflowOperationException(
163 String.format("Could not move %s to media package %s", croppedTrack.getURI(),
164 mediaPackage.getIdentifier()));
165 }
166
167
168 applyTargetTagsToElement(targetTags, croppedTrack);
169 croppedTrack.setFlavor(targetFlavor);
170
171
172 mediaPackage.addDerived(croppedTrack, track);
173
174 totalTimeInQueue += job.getQueueTime() == null ? 0 : job.getQueueTime();
175 }
176 logger.info("Video cropping completed");
177 return createResult(mediaPackage, WorkflowOperationResult.Action.CONTINUE, totalTimeInQueue);
178 }
179
180
181
182
183
184
185
186
187 @Reference
188 protected void setCropService(CropService cropService) {
189 this.cropService = cropService;
190 }
191
192
193
194
195
196
197
198
199 @Reference
200 protected void setWorkspace(Workspace workspace) {
201 this.workspace = workspace;
202 }
203
204 @Reference
205 @Override
206 public void setServiceRegistry(ServiceRegistry serviceRegistry) {
207 super.setServiceRegistry(serviceRegistry);
208 }
209
210 }