View Javadoc
1   /*
2    * Licensed to The Apereo Foundation under one or more contributor license
3    * agreements. See the NOTICE file distributed with this work for additional
4    * information regarding copyright ownership.
5    *
6    *
7    * The Apereo Foundation licenses this file to you under the Educational
8    * Community License, Version 2.0 (the "License"); you may not use this file
9    * except in compliance with the License. You may obtain a copy of the License
10   * at:
11   *
12   *   http://opensource.org/licenses/ecl2.txt
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
17   * License for the specific language governing permissions and limitations under
18   * the License.
19   *
20   */
21  
22  package org.opencastproject.inspection.ffmpeg;
23  
24  import org.opencastproject.inspection.api.MediaInspectionException;
25  import org.opencastproject.inspection.api.MediaInspectionService;
26  import org.opencastproject.inspection.api.util.Options;
27  import org.opencastproject.job.api.AbstractJobProducer;
28  import org.opencastproject.job.api.Job;
29  import org.opencastproject.mediapackage.MediaPackageElement;
30  import org.opencastproject.mediapackage.MediaPackageElementParser;
31  import org.opencastproject.mediapackage.MediaPackageException;
32  import org.opencastproject.security.api.OrganizationDirectoryService;
33  import org.opencastproject.security.api.SecurityService;
34  import org.opencastproject.security.api.UserDirectoryService;
35  import org.opencastproject.serviceregistry.api.ServiceRegistry;
36  import org.opencastproject.serviceregistry.api.ServiceRegistryException;
37  import org.opencastproject.util.LoadUtil;
38  import org.opencastproject.workspace.api.Workspace;
39  
40  import org.osgi.service.component.ComponentContext;
41  import org.osgi.service.component.annotations.Activate;
42  import org.osgi.service.component.annotations.Component;
43  import org.osgi.service.component.annotations.Modified;
44  import org.osgi.service.component.annotations.Reference;
45  import org.slf4j.Logger;
46  import org.slf4j.LoggerFactory;
47  
48  import java.net.URI;
49  import java.util.Arrays;
50  import java.util.Dictionary;
51  import java.util.List;
52  import java.util.Map;
53  
54  /** Inspects media via ffprobe. */
55  @Component(
56    property = {
57      "service.description=Media Inspection Service",
58      "service.pid=org.opencastproject.inspection.ffmpeg.MediaInspectionServiceImpl"
59    },
60    immediate = true,
61    service = { MediaInspectionService.class }
62  )
63  public class MediaInspectionServiceImpl extends AbstractJobProducer implements MediaInspectionService {
64  
65    /** The load introduced on the system by creating an inspect job */
66    public static final float DEFAULT_INSPECT_JOB_LOAD = 0.2f;
67  
68    /** The load introduced on the system by creating an enrich job */
69    public static final float DEFAULT_ENRICH_JOB_LOAD = 0.2f;
70  
71    /** The key to look for in the service configuration file to override the {@link DEFAULT_INSPECT_JOB_LOAD} */
72    public static final String INSPECT_JOB_LOAD_KEY = "job.load.inspect";
73  
74    /** The key to look for in the service configuration file to override the {@link DEFAULT_ENRICH_JOB_LOAD} */
75    public static final String ENRICH_JOB_LOAD_KEY = "job.load.enrich";
76  
77    /** The load introduced on the system by creating an inspect job */
78    private float inspectJobLoad = DEFAULT_INSPECT_JOB_LOAD;
79  
80    /** The load introduced on the system by creating an enrich job */
81    private float enrichJobLoad = DEFAULT_ENRICH_JOB_LOAD;
82  
83    private static final Logger logger = LoggerFactory.getLogger(MediaInspectionServiceImpl.class);
84  
85    /** List of available operations on jobs */
86    private enum Operation {
87      Inspect, Enrich
88    }
89  
90    private Workspace workspace;
91    private ServiceRegistry serviceRegistry;
92    private SecurityService securityService = null;
93    private UserDirectoryService userDirectoryService = null;
94    private OrganizationDirectoryService organizationDirectoryService = null;
95  
96    private volatile MediaInspector inspector;
97  
98    /** Creates a new media inspection service instance. */
99    public MediaInspectionServiceImpl() {
100     super(JOB_TYPE);
101   }
102 
103   @Override
104   @Activate
105   public void activate(ComponentContext cc) {
106     super.activate(cc);
107     /* Configure analyzer */
108     final String path = cc.getBundleContext().getProperty(FFmpegAnalyzer.FFPROBE_BINARY_CONFIG);
109     final String ffprobeBinary;
110     if (path == null) {
111       logger.debug("DEFAULT " + FFmpegAnalyzer.FFPROBE_BINARY_CONFIG + ": " + FFmpegAnalyzer.FFPROBE_BINARY_DEFAULT);
112       ffprobeBinary = FFmpegAnalyzer.FFPROBE_BINARY_DEFAULT;
113     } else {
114       logger.debug("FFprobe config binary: {}", path);
115       ffprobeBinary = path;
116     }
117     inspector = new MediaInspector(workspace, ffprobeBinary);
118   }
119 
120   @Modified
121   public void modified(ComponentContext cc) {
122     Dictionary<String, Object> properties = cc.getProperties();
123     if (properties == null) {
124       logger.info("No configuration available, using defaults");
125       return;
126     }
127     inspectJobLoad = LoadUtil.getConfiguredLoadValue(properties, INSPECT_JOB_LOAD_KEY, DEFAULT_INSPECT_JOB_LOAD,
128             serviceRegistry);
129     enrichJobLoad = LoadUtil.getConfiguredLoadValue(properties, ENRICH_JOB_LOAD_KEY, DEFAULT_ENRICH_JOB_LOAD,
130             serviceRegistry);
131     logger.info("Configuration updated");
132   }
133 
134   /**
135    * {@inheritDoc}
136    *
137    * @see org.opencastproject.job.api.AbstractJobProducer#process(org.opencastproject.job.api.Job)
138    */
139   @Override
140   protected String process(Job job) throws Exception {
141     Operation op = null;
142     String operation = job.getOperation();
143     List<String> arguments = job.getArguments();
144     try {
145       op = Operation.valueOf(operation);
146       MediaPackageElement inspectedElement = null;
147       Map<String, String> options = null;
148       switch (op) {
149         case Inspect:
150           URI uri = URI.create(arguments.get(0));
151           options = Options.fromJson(arguments.get(1));
152           inspectedElement = inspector.inspectTrack(uri, options);
153           break;
154         case Enrich:
155           MediaPackageElement element = MediaPackageElementParser.getFromXml(arguments.get(0));
156           boolean overwrite = Boolean.parseBoolean(arguments.get(1));
157           options = Options.fromJson(arguments.get(2));
158           inspectedElement = inspector.enrich(element, overwrite, options);
159           break;
160         default:
161           throw new IllegalStateException("Don't know how to handle operation '" + operation + "'");
162       }
163       return MediaPackageElementParser.getAsXml(inspectedElement);
164     } catch (IllegalArgumentException e) {
165       throw new ServiceRegistryException("This service can't handle operations of type '" + op + "'", e);
166     } catch (IndexOutOfBoundsException e) {
167       throw new ServiceRegistryException("This argument list for operation '" + op + "' does not meet expectations", e);
168     } catch (Exception e) {
169       throw new ServiceRegistryException("Error handling operation '" + op + "'", e);
170     }
171   }
172 
173   /**
174    * {@inheritDoc}
175    *
176    * @see org.opencastproject.inspection.api.MediaInspectionService#inspect(java.net.URI)
177    */
178   @Override
179   public Job inspect(URI uri) throws MediaInspectionException {
180     return inspect(uri, Options.NO_OPTION);
181   }
182 
183   /**
184    * {@inheritDoc}
185    *
186    * @see org.opencastproject.inspection.api.MediaInspectionService#inspect(java.net.URI, java.util.Map)
187    */
188   @Override
189   public Job inspect(URI uri, final Map<String,String> options) throws MediaInspectionException {
190     assert (options != null);
191     try {
192       return serviceRegistry.createJob(JOB_TYPE, Operation.Inspect.toString(), Arrays.asList(uri.toString(),
193               Options.toJson(options)), inspectJobLoad);
194     } catch (ServiceRegistryException e) {
195       throw new MediaInspectionException(e);
196     }
197   }
198 
199   /**
200    * {@inheritDoc}
201    *
202    * @see org.opencastproject.inspection.api.MediaInspectionService#enrich(org.opencastproject.mediapackage.MediaPackageElement,
203    *      boolean)
204    */
205   @Override
206   public Job enrich(final MediaPackageElement element, final boolean override)
207           throws MediaInspectionException, MediaPackageException {
208     return enrich(element, override, Options.NO_OPTION);
209   }
210 
211   /**
212    * {@inheritDoc}
213    *
214    * @see org.opencastproject.inspection.api.MediaInspectionService#enrich(org.opencastproject.mediapackage.MediaPackageElement,
215    *      boolean, java.util.Map)
216    */
217   @Override
218   public Job enrich(final MediaPackageElement element, final boolean override, final Map<String,String> options)
219           throws MediaInspectionException, MediaPackageException {
220     assert (options != null);
221     try {
222       return serviceRegistry.createJob(JOB_TYPE, Operation.Enrich.toString(),
223               Arrays.asList(MediaPackageElementParser.getAsXml(element), Boolean.toString(override),
224               Options.toJson(options)), enrichJobLoad);
225     } catch (ServiceRegistryException e) {
226       throw new MediaInspectionException(e);
227     }
228   }
229 
230   @Reference
231   protected void setWorkspace(Workspace workspace) {
232     logger.debug("setting " + workspace);
233     this.workspace = workspace;
234   }
235 
236   @Reference
237   protected void setServiceRegistry(ServiceRegistry jobManager) {
238     this.serviceRegistry = jobManager;
239   }
240 
241   /**
242    * {@inheritDoc}
243    *
244    * @see org.opencastproject.job.api.AbstractJobProducer#getServiceRegistry()
245    */
246   @Override
247   protected ServiceRegistry getServiceRegistry() {
248     return serviceRegistry;
249   }
250 
251   /**
252    * Callback for setting the security service.
253    *
254    * @param securityService
255    *          the securityService to set
256    */
257   @Reference
258   public void setSecurityService(SecurityService securityService) {
259     this.securityService = securityService;
260   }
261 
262   /**
263    * Callback for setting the user directory service.
264    *
265    * @param userDirectoryService
266    *          the userDirectoryService to set
267    */
268   @Reference
269   public void setUserDirectoryService(UserDirectoryService userDirectoryService) {
270     this.userDirectoryService = userDirectoryService;
271   }
272 
273   /**
274    * Sets a reference to the organization directory service.
275    *
276    * @param organizationDirectory
277    *          the organization directory
278    */
279   @Reference
280   public void setOrganizationDirectoryService(OrganizationDirectoryService organizationDirectory) {
281     this.organizationDirectoryService = organizationDirectory;
282   }
283 
284   /**
285    * {@inheritDoc}
286    *
287    * @see org.opencastproject.job.api.AbstractJobProducer#getSecurityService()
288    */
289   @Override
290   protected SecurityService getSecurityService() {
291     return securityService;
292   }
293 
294   /**
295    * {@inheritDoc}
296    *
297    * @see org.opencastproject.job.api.AbstractJobProducer#getUserDirectoryService()
298    */
299   @Override
300   protected UserDirectoryService getUserDirectoryService() {
301     return userDirectoryService;
302   }
303 
304   /**
305    * {@inheritDoc}
306    *
307    * @see org.opencastproject.job.api.AbstractJobProducer#getOrganizationDirectoryService()
308    */
309   @Override
310   protected OrganizationDirectoryService getOrganizationDirectoryService() {
311     return organizationDirectoryService;
312   }
313 }