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