1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.opencastproject.workflow.handler.composer;
23
24 import org.opencastproject.composer.api.ComposerService;
25 import org.opencastproject.composer.api.EncoderException;
26 import org.opencastproject.composer.api.EncodingProfile;
27 import org.opencastproject.composer.api.EncodingProfile.MediaType;
28 import org.opencastproject.job.api.Job;
29 import org.opencastproject.job.api.JobContext;
30 import org.opencastproject.mediapackage.AdaptivePlaylist;
31 import org.opencastproject.mediapackage.Catalog;
32 import org.opencastproject.mediapackage.MediaPackage;
33 import org.opencastproject.mediapackage.MediaPackageElementFlavor;
34 import org.opencastproject.mediapackage.MediaPackageElementParser;
35 import org.opencastproject.mediapackage.MediaPackageException;
36 import org.opencastproject.mediapackage.Track;
37 import org.opencastproject.mediapackage.selector.AbstractMediaPackageElementSelector;
38 import org.opencastproject.mediapackage.selector.TrackSelector;
39 import org.opencastproject.serviceregistry.api.ServiceRegistry;
40 import org.opencastproject.smil.api.SmilException;
41 import org.opencastproject.smil.api.SmilResponse;
42 import org.opencastproject.smil.api.SmilService;
43 import org.opencastproject.smil.entity.api.Smil;
44 import org.opencastproject.smil.entity.media.param.api.SmilMediaParam;
45 import org.opencastproject.smil.entity.media.param.api.SmilMediaParamGroup;
46 import org.opencastproject.util.NotFoundException;
47 import org.opencastproject.workflow.api.AbstractWorkflowOperationHandler;
48 import org.opencastproject.workflow.api.WorkflowInstance;
49 import org.opencastproject.workflow.api.WorkflowOperationException;
50 import org.opencastproject.workflow.api.WorkflowOperationHandler;
51 import org.opencastproject.workflow.api.WorkflowOperationInstance;
52 import org.opencastproject.workflow.api.WorkflowOperationResult;
53 import org.opencastproject.workflow.api.WorkflowOperationResult.Action;
54 import org.opencastproject.workspace.api.Workspace;
55
56 import org.apache.commons.io.FileUtils;
57 import org.apache.commons.io.FilenameUtils;
58 import org.apache.commons.lang3.StringUtils;
59 import org.osgi.service.component.ComponentContext;
60 import org.osgi.service.component.annotations.Activate;
61 import org.osgi.service.component.annotations.Component;
62 import org.osgi.service.component.annotations.Reference;
63 import org.slf4j.Logger;
64 import org.slf4j.LoggerFactory;
65
66 import java.io.File;
67 import java.io.IOException;
68 import java.net.URI;
69 import java.net.URISyntaxException;
70 import java.util.ArrayList;
71 import java.util.Arrays;
72 import java.util.Collection;
73 import java.util.HashMap;
74 import java.util.HashSet;
75 import java.util.Iterator;
76 import java.util.List;
77 import java.util.Map;
78 import java.util.Set;
79 import java.util.function.Predicate;
80 import java.util.stream.Collectors;
81
82
83
84
85 @Component(
86 immediate = true,
87 service = WorkflowOperationHandler.class,
88 property = {
89 "service.description=Process Smil Workflow Operation Handler",
90 "workflow.operation=process-smil"
91 }
92 )
93 public class ProcessSmilWorkflowOperationHandler extends AbstractWorkflowOperationHandler {
94 static final String SEPARATOR = ";";
95
96 private static final Logger logger = LoggerFactory.getLogger(ProcessSmilWorkflowOperationHandler.class);
97
98
99 private ComposerService composerService = null;
100
101 private SmilService smilService;
102
103 private Workspace workspace = null;
104
105 private Predicate<EncodingProfile> isManifestEP = p -> p.getOutputType() == EncodingProfile.MediaType.Manifest;
106
107
108
109
110 private class TrackSection {
111 private final String paramGroupId;
112 private List<Track> sourceTracks;
113 private List<String> smilTracks;
114 private final String flavor;
115 private String mediaType = "";
116
117 TrackSection(String id, String flavor) {
118 this.flavor = flavor;
119 this.paramGroupId = id;
120 }
121
122 public List<Track> getSourceTracks() {
123 return sourceTracks;
124 }
125
126
127
128
129
130
131
132 public void setSourceTracks(List<Track> sourceTracks) {
133 boolean hasVideo = true;
134 boolean hasAudio = true;
135 this.sourceTracks = sourceTracks;
136 for (Track track : sourceTracks) {
137 if (!track.hasVideo()) {
138 hasVideo = false;
139 }
140 if (!track.hasAudio()) {
141 hasAudio = false;
142 }
143 }
144 if (!hasVideo) {
145 mediaType = ComposerService.AUDIO_ONLY;
146 }
147 if (!hasAudio) {
148 mediaType = ComposerService.VIDEO_ONLY;
149 }
150 }
151
152 public String getFlavor() {
153 return flavor;
154 }
155
156 @Override
157 public String toString() {
158 return paramGroupId + " " + flavor + " " + sourceTracks.toString();
159 }
160
161 public void setSmilTrackList(List<String> smilSourceTracks) {
162 smilTracks = smilSourceTracks;
163 }
164
165 public List<String> getSmilTrackList() {
166 return smilTracks;
167 }
168 };
169
170
171 private class ResultTally {
172 private final MediaPackage mediaPackage;
173 private final long totalTimeInQueue;
174
175 ResultTally(MediaPackage mediaPackage, long totalTimeInQueue) {
176 super();
177 this.mediaPackage = mediaPackage;
178 this.totalTimeInQueue = totalTimeInQueue;
179 }
180
181 public MediaPackage getMediaPackage() {
182 return mediaPackage;
183 }
184
185 public long getTotalTimeInQueue() {
186 return totalTimeInQueue;
187 }
188 }
189
190 @Activate
191 public void activate(ComponentContext cc) {
192 super.activate(cc);
193 }
194
195
196
197
198
199
200
201 @Reference
202 protected void setComposerService(ComposerService composerService) {
203 this.composerService = composerService;
204 }
205
206
207
208
209
210
211 @Reference
212 protected void setSmilService(SmilService smilService) {
213 this.smilService = smilService;
214 }
215
216
217
218
219
220
221
222
223 @Reference
224 public void setWorkspace(Workspace workspace) {
225 this.workspace = workspace;
226 }
227
228 @Reference
229 @Override
230 public void setServiceRegistry(ServiceRegistry serviceRegistry) {
231 super.setServiceRegistry(serviceRegistry);
232 }
233
234
235
236
237
238
239
240 @Override
241 public WorkflowOperationResult start(final WorkflowInstance workflowInstance, JobContext context)
242 throws WorkflowOperationException {
243 try {
244 return processSmil(workflowInstance.getMediaPackage(), workflowInstance.getCurrentOperation());
245 } catch (Exception e) {
246 e.printStackTrace();
247 throw new WorkflowOperationException(e);
248 }
249 }
250
251 private String[] getConfigAsArray(WorkflowOperationInstance operation, String name) {
252 String sourceOption = StringUtils.trimToNull(operation.getConfiguration(name));
253 String[] options = (sourceOption != null) ? sourceOption.split(SEPARATOR) : null;
254 return (options);
255 }
256
257 private String[] collapseConfig(WorkflowOperationInstance operation, String name) {
258 String targetOption = StringUtils.trimToNull(operation.getConfiguration(name));
259 return (targetOption != null) ? new String[] { targetOption.replaceAll(SEPARATOR, ",") } : null;
260 }
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280 private WorkflowOperationResult processSmil(MediaPackage src, WorkflowOperationInstance operation)
281 throws EncoderException, IOException, NotFoundException, MediaPackageException, WorkflowOperationException {
282 MediaPackage mediaPackage = (MediaPackage) src.clone();
283
284 String smilFlavorOption = StringUtils.trimToEmpty(operation.getConfiguration("smil-flavor"));
285 String[] srcFlavors = getConfigAsArray(operation, "source-flavors");
286 String[] targetFlavors = getConfigAsArray(operation, "target-flavors");
287 String[] targetTags = getConfigAsArray(operation, "target-tags");
288 String[] profilesSections = getConfigAsArray(operation, "encoding-profiles");
289 String tagWithProfileConfig = StringUtils.trimToNull(operation.getConfiguration("tag-with-profile"));
290 boolean tagWithProfile = tagWithProfileConfig != null && Boolean.parseBoolean(tagWithProfileConfig);
291
292
293 if (StringUtils.isBlank(smilFlavorOption)) {
294 logger.info("No smil flavor has been specified, no src to process");
295 return createResult(mediaPackage, Action.CONTINUE);
296 }
297
298 if (srcFlavors == null) {
299 logger.info("No source flavors have been specified, not matching anything");
300 return createResult(mediaPackage, Action.CONTINUE);
301 }
302
303 if (profilesSections == null) {
304 throw new WorkflowOperationException("No encoding profile was specified");
305 }
306
307
308
309
310
311
312 if (srcFlavors.length > 1) {
313 if (targetFlavors != null && srcFlavors.length != targetFlavors.length && targetFlavors.length != 1) {
314 String mesg = "Number of target flavor sections " + targetFlavors + " must either match that of src flavor "
315 + srcFlavors + " or equal 1 ";
316 throw new WorkflowOperationException(mesg);
317 }
318 if (srcFlavors.length != profilesSections.length) {
319 if (profilesSections.length != 1) {
320 String mesg = "Number of encoding profile sections " + profilesSections
321 + " must either match that of src flavor " + srcFlavors + " or equal 1 ";
322 throw new WorkflowOperationException(mesg);
323 } else {
324 String[] array = new String[srcFlavors.length];
325 Arrays.fill(array, 0, srcFlavors.length, profilesSections[0]);
326 profilesSections = array;
327 }
328 }
329 if (targetTags != null && srcFlavors.length != targetTags.length && targetTags.length != 1) {
330 String mesg = "Number of target Tags sections " + targetTags + " must either match that of src flavor "
331 + srcFlavors + " or equal 1 ";
332 throw new WorkflowOperationException(mesg);
333 }
334 } else {
335 targetFlavors = collapseConfig(operation, "target-flavors");
336 targetTags = collapseConfig(operation, "target-tags");
337 profilesSections = collapseConfig(operation, "encoding-profiles");
338 if (profilesSections.length != 1) {
339 throw new WorkflowOperationException(
340 "No matching src flavors " + srcFlavors + " for encoding profiles sections " + profilesSections);
341 }
342
343 logger.debug("Single input flavor: output= " + Arrays.toString(targetFlavors) + " tag: "
344 + Arrays.toString(targetTags) + " profile:" + Arrays.toString(profilesSections));
345 }
346
347 Map<Job, JobInformation> encodingJobs = new HashMap<Job, JobInformation>();
348 for (int i = 0; i < profilesSections.length; i++) {
349
350 processSection(encodingJobs, mediaPackage, (srcFlavors.length > 1) ? srcFlavors[i] : srcFlavors[0],
351 (targetFlavors != null) ? ((targetFlavors.length > 1) ? targetFlavors[i] : targetFlavors[0]) : null,
352 (targetTags != null) ? ((targetTags.length > 1) ? targetTags[i] : targetTags[0]) : null,
353 (profilesSections.length > 0) ? profilesSections[i] : profilesSections[0], smilFlavorOption,
354 tagWithProfile);
355 }
356
357 if (encodingJobs.isEmpty()) {
358 logger.info("Failed to process any tracks");
359 return createResult(mediaPackage, Action.CONTINUE);
360 }
361
362
363 if (!waitForStatus(encodingJobs.keySet().toArray(new Job[encodingJobs.size()])).isSuccess()) {
364 throw new WorkflowOperationException("One of the encoding jobs did not complete successfully");
365 }
366 ResultTally allResults = parseResults(encodingJobs, mediaPackage);
367 WorkflowOperationResult result = createResult(allResults.getMediaPackage(), Action.CONTINUE,
368 allResults.getTotalTimeInQueue());
369 logger.debug("ProcessSmil operation completed");
370 return result;
371
372 }
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402 private void processSection(Map<Job, JobInformation> encodingJobs, MediaPackage mediaPackage,
403 String srcFlavors, String targetFlavors, String targetTags,
404 String encodingProfiles, String smilFlavor, boolean tagWithProfile) throws WorkflowOperationException,
405 EncoderException, MediaPackageException, IllegalArgumentException, NotFoundException, IOException {
406
407 AbstractMediaPackageElementSelector<Track> elementSelector = new TrackSelector();
408 for (String flavor : asList(srcFlavors)) {
409 try {
410 elementSelector.addFlavor(MediaPackageElementFlavor.parseFlavor(flavor));
411 } catch (IllegalArgumentException e) {
412 throw new WorkflowOperationException("Source flavor '" + flavor + "' is malformed");
413 }
414 }
415 Smil smil = getSmil(mediaPackage, smilFlavor);
416
417 List<TrackSection> smilgroups;
418 try {
419 smilgroups = selectTracksFromMP(mediaPackage, smil, srcFlavors);
420 } catch (URISyntaxException e1) {
421 logger.info("Smil contains bad URI", e1);
422 throw new WorkflowOperationException("Smil contains bad URI - cannot process", e1);
423 }
424 if (smilgroups.size() == 0 || smilgroups.get(0).sourceTracks.size() == 0) {
425 logger.info("Smil does not contain any tracks of {} source flavor", srcFlavors);
426 return;
427 }
428
429
430 MediaPackageElementFlavor targetFlavor = null;
431 if (StringUtils.isNotBlank(targetFlavors)) {
432 try {
433 targetFlavor = MediaPackageElementFlavor.parseFlavor(targetFlavors);
434 } catch (IllegalArgumentException e) {
435 throw new WorkflowOperationException("Target flavor '" + targetFlavors + "' is malformed");
436 }
437 }
438
439 Set<EncodingProfile> profiles = new HashSet<EncodingProfile>();
440 Set<String> profileNames = new HashSet<String>();
441
442
443 for (TrackSection ts : smilgroups) {
444 for (Track track : ts.getSourceTracks()) {
445
446 for (String profileName : asList(encodingProfiles)) {
447 EncodingProfile profile = composerService.getProfile(profileName);
448 if (profile == null) {
449 throw new WorkflowOperationException("Encoding profile '" + profileName + "' was not found");
450 }
451 MediaType outputType = profile.getOutputType();
452
453
454 if (outputType.equals(MediaType.Audio) && !track.hasAudio()) {
455 logger.info("Skipping encoding of '{}' with " + profileName + ", since the track lacks an audio stream",
456 track);
457 continue;
458 } else if (outputType.equals(MediaType.Visual) && !track.hasVideo()) {
459 logger.info("Skipping encoding of '{}' " + profileName + ", since the track lacks a video stream", track);
460 continue;
461 } else if (outputType.equals(MediaType.AudioVisual) && !track.hasAudio() && !track.hasVideo()) {
462 logger.info("Skipping encoding of '{}' (audiovisual)" + profileName
463 + ", since it lacks a audio or video stream", track);
464 continue;
465 }
466 profiles.add(profile);
467 profileNames.add(profileName);
468 }
469 }
470 }
471
472 if (profiles.isEmpty()) {
473 throw new WorkflowOperationException("No encoding profile was specified");
474 }
475
476 List<String> tags = (targetTags != null) ? asList(targetTags) : null;
477
478
479 for (TrackSection trackGroup : smilgroups) {
480 encodingJobs.put(
481 composerService.processSmil(smil, trackGroup.paramGroupId, trackGroup.mediaType,
482 new ArrayList<String>(profileNames)),
483 new JobInformation(trackGroup.paramGroupId, trackGroup.sourceTracks,
484 new ArrayList<EncodingProfile>(profiles), tags, targetFlavor, tagWithProfile));
485
486 logger.info("Edit and encode {} target flavors: {} tags: {} profile {}", trackGroup, targetFlavor, tags,
487 profileNames);
488 }
489 }
490
491
492
493
494
495
496
497
498
499 private void tagByProfile(Track track, List<EncodingProfile> profiles) {
500 String rawfileName = track.getURI().getRawPath();
501 for (EncodingProfile ep : profiles) {
502
503
504
505 String suffixToSanitize = "X" + ep.getSuffix();
506
507 String suffix = workspace.toSafeName(suffixToSanitize).substring(1);
508 if (suffix.length() > 0 && rawfileName.endsWith(suffix)) {
509 track.addTag(ep.getIdentifier());
510 return;
511 }
512 }
513 }
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531 @SuppressWarnings("unchecked")
532 private ResultTally parseResults(Map<Job, JobInformation> encodingJobs, MediaPackage mediaPackage)
533 throws IllegalArgumentException, NotFoundException, IOException, MediaPackageException,
534 WorkflowOperationException {
535
536 long totalTimeInQueue = 0;
537 for (Map.Entry<Job, JobInformation> entry : encodingJobs.entrySet()) {
538 Job job = entry.getKey();
539 List<Track> tracks = entry.getValue().getTracks();
540 Track track = tracks.get(0);
541
542 totalTimeInQueue += job.getQueueTime();
543
544 List<Track> composedTracks = null;
545 if (job.getPayload().length() > 0) {
546 composedTracks = (List<Track>) MediaPackageElementParser.getArrayFromXml(job.getPayload());
547 boolean isHLS = entry.getValue().getProfiles().stream().anyMatch(isManifestEP);
548 if (isHLS) {
549 decipherHLSPlaylistResults(track, entry.getValue(), mediaPackage, composedTracks);
550 }
551
552 for (Track composedTrack : composedTracks) {
553 if (entry.getValue().getTags() != null) {
554 for (String tag : entry.getValue().getTags()) {
555 composedTrack.addTag(tag);
556 }
557 }
558
559 MediaPackageElementFlavor targetFlavor = entry.getValue().getFlavor();
560 if (targetFlavor != null) {
561 String flavorType = targetFlavor.getType();
562 String flavorSubtype = targetFlavor.getSubtype();
563 if ("*".equals(flavorType)) {
564 flavorType = track.getFlavor().getType();
565 }
566 if ("*".equals(flavorSubtype)) {
567 flavorSubtype = track.getFlavor().getSubtype();
568 }
569 composedTrack.setFlavor(new MediaPackageElementFlavor(flavorType, flavorSubtype));
570 logger.debug("Composed track has flavor '{}'", composedTrack.getFlavor());
571 }
572 List<EncodingProfile> eps = entry.getValue().getProfiles();
573 String fileName = composedTrack.getURI().getRawPath();
574
575 if (entry.getValue().getTagProfile()) {
576 tagByProfile(composedTrack, eps);
577 }
578
579 if (!isHLS || composedTrack.isMaster()) {
580 fileName = getFileNameFromElements(track, composedTrack);
581 } else {
582 fileName = FilenameUtils.getName(composedTrack.getURI().getPath());
583 }
584
585 composedTrack.setURI(workspace.moveTo(composedTrack.getURI(), mediaPackage.getIdentifier().toString(),
586 composedTrack.getIdentifier(), fileName));
587 synchronized (mediaPackage) {
588 mediaPackage.addDerived(composedTrack, track);
589 }
590 }
591 }
592 }
593 return new ResultTally(mediaPackage, totalTimeInQueue);
594 }
595
596 private List<Track> getManifest(Collection<Track> tracks) {
597 return tracks.stream().filter(AdaptivePlaylist.isHLSTrackPred).collect(Collectors.toList());
598 }
599
600
601 private void decipherHLSPlaylistResults(Track track, JobInformation jobInfo, MediaPackage mediaPackage,
602 List<Track> composedTracks)
603 throws WorkflowOperationException, IllegalArgumentException, NotFoundException, IOException {
604 int nprofiles = jobInfo.getProfiles().size();
605 List<Track> manifests = getManifest(composedTracks);
606
607 if (manifests.size() != nprofiles) {
608 throw new WorkflowOperationException("Number of output playlists does not match number of encoding profiles");
609 }
610 if (composedTracks.size() != manifests.size() * 2 - 1) {
611 throw new WorkflowOperationException("Number of output media does not match number of encoding profiles");
612 }
613 }
614
615
616
617
618
619
620 private boolean trackMatchesFlavor(MediaPackageElementFlavor trackFlavor, MediaPackageElementFlavor sourceFlavor) {
621 return ((trackFlavor.getType().equals(sourceFlavor.getType()) && trackFlavor.getSubtype()
622 .equals(sourceFlavor.getSubtype()))
623
624 || ("*".equals(sourceFlavor.getType()) && trackFlavor.getSubtype().equals(sourceFlavor.getSubtype()))
625
626 || (trackFlavor.getType().equals(sourceFlavor.getType()) && "*".equals(sourceFlavor.getSubtype())));
627 }
628
629
630
631
632
633
634
635
636
637
638
639
640 private List<TrackSection> selectTracksFromMP(MediaPackage mediaPackage, Smil smil, String srcFlavors)
641 throws WorkflowOperationException, URISyntaxException {
642 List<TrackSection> sourceTrackList = new ArrayList<TrackSection>();
643 Collection<TrackSection> smilFlavors = parseSmil(smil);
644 Iterator<TrackSection> it = smilFlavors.iterator();
645 while (it.hasNext()) {
646 TrackSection ts = it.next();
647
648 for (String f : StringUtils.split(srcFlavors, ",")) {
649 String sourceFlavorStr = StringUtils.trimToNull(f);
650 if (sourceFlavorStr == null) {
651 continue;
652 }
653 MediaPackageElementFlavor sourceFlavor = MediaPackageElementFlavor.parseFlavor(sourceFlavorStr);
654 MediaPackageElementFlavor trackFlavor = MediaPackageElementFlavor.parseFlavor(ts.getFlavor());
655
656 if (trackMatchesFlavor(trackFlavor, sourceFlavor)) {
657 sourceTrackList.add(ts);
658 Track[] elements = null;
659 List<Track> sourceTracks = new ArrayList<Track>();
660 elements = mediaPackage.getTracks(sourceFlavor);
661 for (String t : ts.getSmilTrackList()) {
662 URI turi = new URI(t);
663 for (Track e : elements) {
664 if (e.getURI().equals(turi)) {
665 sourceTracks.add(e);
666 }
667 }
668 }
669 if (sourceTracks.isEmpty()) {
670 logger.info("ProcessSmil - No tracks in mediapackage matching the URI in the smil- cannot process");
671 throw new WorkflowOperationException("Smil has no matching tracks in the mediapackage");
672 }
673 ts.setSourceTracks(sourceTracks);
674 }
675 }
676 }
677 return sourceTrackList;
678 }
679
680
681
682
683
684
685
686
687
688 private Smil getSmil(MediaPackage mp, String smilFlavorOption) throws WorkflowOperationException {
689 MediaPackageElementFlavor smilFlavor = MediaPackageElementFlavor.parseFlavor(smilFlavorOption);
690 Catalog[] catalogs = mp.getCatalogs(smilFlavor);
691 if (catalogs.length == 0) {
692 throw new WorkflowOperationException("MediaPackage does not contain a SMIL document.");
693 }
694 Smil smil = null;
695 try {
696 File smilFile = workspace.get(catalogs[0].getURI());
697
698 SmilResponse response = smilService.fromXml(FileUtils.readFileToString(smilFile, "UTF-8"));
699 smil = response.getSmil();
700 return smil;
701 } catch (NotFoundException ex) {
702 throw new WorkflowOperationException("MediaPackage does not contain a smil catalog.");
703 } catch (IOException ex) {
704 throw new WorkflowOperationException("Failed to read smil catalog.", ex);
705 } catch (SmilException ex) {
706 throw new WorkflowOperationException(ex);
707 }
708 }
709
710
711
712
713
714
715
716 private Collection<TrackSection> parseSmil(Smil smil) {
717
718 List<TrackSection> trackGroups = new ArrayList<TrackSection>();
719
720 for (SmilMediaParamGroup paramGroup : smil.getHead().getParamGroups()) {
721 TrackSection ts = null;
722 List<String> src = new ArrayList<String>();
723 for (SmilMediaParam param : paramGroup.getParams()) {
724 if (SmilMediaParam.PARAM_NAME_TRACK_FLAVOR.matches(param.getName())) {
725 ts = new TrackSection(paramGroup.getId(), param.getValue());
726 trackGroups.add(ts);
727 }
728 if (SmilMediaParam.PARAM_NAME_TRACK_SRC.matches(param.getName())) {
729 src.add(param.getValue());
730 }
731 }
732 if (ts != null) {
733 ts.setSmilTrackList(src);
734 }
735 }
736 return trackGroups;
737 }
738
739
740
741
742 private static final class JobInformation {
743
744 private final List<EncodingProfile> profiles;
745 private final List<Track> tracks;
746 private String grp = null;
747 private MediaPackageElementFlavor flavor = null;
748 private List<String> tags = null;
749 private boolean tagProfile;
750
751 JobInformation(String paramgroup, List<Track> tracks, List<EncodingProfile> profiles, List<String> tags,
752 MediaPackageElementFlavor flavor, boolean tagWithProfile) {
753 this.tracks = tracks;
754 this.grp = paramgroup;
755 this.profiles = profiles;
756 this.tags = tags;
757 this.flavor = flavor;
758 this.tagProfile = tagWithProfile;
759 }
760
761 public List<Track> getTracks() {
762 return tracks;
763 }
764
765 public MediaPackageElementFlavor getFlavor() {
766 return flavor;
767 }
768
769 public List<String> getTags() {
770 return tags;
771 }
772
773 public boolean getTagProfile() {
774 return this.tagProfile;
775 }
776
777 @SuppressWarnings("unused")
778 public String getGroups() {
779 return grp;
780 }
781
782 @SuppressWarnings("unused")
783 public List<EncodingProfile> getProfiles() {
784 return profiles;
785 }
786
787 }
788
789 }