/**
* 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.composer.impl.episode;
import org.opencastproject.composer.api.EncoderException;
import org.opencastproject.composer.api.EncodingProfile;
import org.opencastproject.composer.impl.AbstractEncoderEngine;
import org.opencastproject.util.ConfigurationException;
import org.opencastproject.util.PathSupport;
import org.opencastproject.util.data.Option;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.util.List;
import java.util.Map;
import java.util.Properties;
/**
* Wrapper for the Telestream Episode Compression Engine.
*/
public final class EpisodeEncoderEngine extends AbstractEncoderEngine {
/** The episode inbox option name */
public static final String OPT_MONITORTYPE = "episode.type";
/** The episode monitor frequency option name */
public static final String OPT_EPISODE_MONITOR_FREQUENCY = "episode.frequency";
/** The episodes xmlrpc host option name */
public static final String OPT_XMLRPC_HOST = "episode.xmlrpc.host";
/** The episodes xmlrpc port option name */
public static final String OPT_XMLRPC_PORT = "episode.xmlrpc.port";
/** The episodes xmlrpc path option name */
public static final String OPT_XMLRPC_PATH = "episode.xmlrpc.path";
/** The episodes xmlrpc password option name */
public static final String OPT_XMLRPC_PASSWORD = "episode.xmlrpc.password";
/** Default monitor interval */
public static final long DEFAULT_MONITOR_FREQUENCY = 60;
/** Default xmlrpc port number */
public static final int DEFAULT_XMLRPC_PORT = 40406;
/** Default xmlrpc password */
public static final String XMLRPC_DEFAULT_PASSWORD = "anonymous";
/** Default xmlrpc path */
public static final String XMLRPC_DEFAULT_PATH = "/RPC2";
/** Medium priority */
public static final int PRIORITY_MEDIUM = 500;
/** Encoding errors */
protected int errors = 0;
/** Xmlrpc hostname */
protected String xmlrpcHostname = null;
/** Xmlrpc port */
protected int xmlrpcPort = DEFAULT_XMLRPC_PORT;
/** Xmlrpc path on the server */
protected String xmlrpcPath = null;
/** Password used to connect to the xmlrpc service */
protected String xmlrpcPassword = null;
/** The monitor frequency */
protected long monitorFrequency = DEFAULT_MONITOR_FREQUENCY;
/** The monitor thread */
private Thread monitorThread = null;
/** The xmlrpc base encoder monitor */
private XmlRpcEngineController xmlrpcController = null;
/** the logging facility provided by log4j */
private static final Logger logger = LoggerFactory.getLogger(EpisodeEncoderEngine.class.getName());
/**
* Creates a new instance of the episode telestream engine wrapper.
*/
public EpisodeEncoderEngine() {
super(true);
}
public void activate(ComponentContext cc) {
// TODO: read configuration data
Properties config = new Properties();
// Use the passed in configuration
configure(config);
// Start the monitor
monitorThread.setDaemon(true);
monitorThread.start(); // Since this is dangerous for subclasses, I've made this class final (jmh)
}
/**
* Configures the compression engine wrapper for use with an xmlrpc controller.
*
* @param properties
* the engine configuration
*/
private void configure(Properties properties) throws ConfigurationException {
try {
// Verify properties is an object
if (properties == null)
throw new ConfigurationException("Properties must not be null");
// Xmlrpc hostname
xmlrpcHostname = (String) properties.get(OPT_XMLRPC_HOST);
logger.debug("Episode xmlrpc host is " + xmlrpcHostname);
// Xmlrpc port
String port = (String) properties.get(OPT_XMLRPC_PORT);
if (port != null && !"".equals(port)) {
try {
xmlrpcPort = Integer.parseInt(port);
} catch (Exception e) {
throw new ConfigurationException("Episode sdk port number '" + port + "' is malformed: " + e.getMessage());
}
}
logger.debug("Episode xmlrpc port number is " + xmlrpcPort);
// Xmlrpc mountpoint
xmlrpcPath = (String) properties.get(OPT_XMLRPC_PATH);
if (xmlrpcPath == null)
throw new ConfigurationException("Episode sdk path not specified");
logger.debug("Episode xmlrpc path is " + xmlrpcPath);
// Xmlrpc password
xmlrpcPassword = (String) properties.get(OPT_XMLRPC_PASSWORD);
if (xmlrpcPath == null) { // FIXME This can never occur, since a config exception would be thrown earlier
xmlrpcPassword = XMLRPC_DEFAULT_PASSWORD;
logger.debug("Episode xmlrpc password was not set, using default");
} else {
logger.debug("Episode xmlrpc password was set to custom value");
}
// Set monitor frequency
try {
monitorFrequency = Long.parseLong((String) properties.get(OPT_EPISODE_MONITOR_FREQUENCY));
} catch (Exception e) {
logger.warn("Unable to set monitorFrequency, are you sure the property is String value that can be parsed to a long? Ignoring and continuing.");
}
logger.debug("Engine updates are gathered every " + monitorFrequency + " s");
// Start the monitor
xmlrpcController = new XmlRpcEngineController(this, xmlrpcHostname, xmlrpcPort, xmlrpcPath, xmlrpcPassword,
monitorFrequency * 1000L);
monitorThread = new Thread(xmlrpcController);
} catch (Exception e) { // FIXME Findbugs says that no exception is thrown here, so this isn't needed
throw new ConfigurationException("Episode engine setup failed: " + e.getMessage());
}
}
/**
* Stops the epsisode compression engine including all their tasks like outfolder watchdogs.
*/
public void stop() {
if (monitorThread != null) {
if (xmlrpcController != null)
xmlrpcController.stop();
monitorThread.interrupt();
xmlrpcController = null;
monitorThread = null;
}
}
/**
* Returns the hostname for the xmlprc server of the episode engine.
* <p>
* The hostname parameter is gathered from the configuration file <code>episode.properties</code>.
* </p>
*
* @return the xmlrpc hostname
*/
public String getXmlrpcHost() {
return xmlrpcHostname;
}
/**
* Returns the port number for the xmlprc server of the episode engine.
* <p>
* The port parameter is gathered from the configuration file <code>episode.properties</code>.
* </p>
*
* @return the xmlrpc hostname
*/
public int getXmlrpcPort() {
return xmlrpcPort;
}
/**
* Returns the path for the xmlprc server of the episode engine.
* <p>
* The path parameter is gathered from the configuration file <code>episode.properties</code>.
* </p>
*
* @return the xmlrpc path on the server
*/
public String getXmlrpcPath() {
return xmlrpcPath;
}
/**
* Returns the number of seconds that are between two calls for a status update.
*
* @return the number of seconds for the monitoring interval
*/
public long getMonitoringFrequency() {
return monitorFrequency;
}
/**
* @see org.opencastproject.composer.api.EncoderEngine#needsLocalWorkCopy()
*/
public boolean needsLocalWorkCopy() {
return true;
}
/**
* {@inheritDoc}
*
* @see org.opencastproject.composer.api.EncoderEngine#mux(java.io.File, java.io.File,
* org.opencastproject.composer.api.EncodingProfile, java.util.Map)
*/
public Option<File> mux(File audioSource, File videoSource, EncodingProfile format, Map<String, String> properties)
throws EncoderException {
throw new UnsupportedOperationException("Not yet implemented");
// xmlrpcController.submitJob(source, format);
// TODO Wait for encoding outcome
// File outputFile = null;
// return outputFile;
}
/**
* {@inheritDoc}
*
* @see org.opencastproject.composer.api.EncoderEngine#encode(java.io.File,
* org.opencastproject.composer.api.EncodingProfile, java.util.Map)
*/
@Override
public Option<File> encode(File mediaSource, EncodingProfile format, Map<String, String> properties)
throws EncoderException {
return mux(null, mediaSource, format, properties);
}
/**
* (non-Javadoc)
*
* @see org.opencastproject.composer.api.EncoderEngine#extract(File, EncodingProfile, Map, double...)
*/
@Override
public List<File> extract(File mediaSource, EncodingProfile format, Map<String, String> properties, double... time)
throws EncoderException {
throw new UnsupportedOperationException("Operation is not supported by episode encoder engine");
}
/**
* {@inheritDoc}
*
* @see org.opencastproject.composer.api.EncoderEngine#trim(java.io.File,
* org.opencastproject.composer.api.EncodingProfile, long, long, java.util.Map)
*/
@Override
public Option<File> trim(File mediaSource, EncodingProfile format, long start, long duration,
Map<String, String> properties) throws EncoderException {
// TODO: Implement
throw new UnsupportedOperationException("Not yet implemented");
}
/**
* {@inheritDoc}
*
* @see org.opencastproject.composer.impl.AbstractEncoderEngine#getOutputFile(java.io.File,
* org.opencastproject.composer.api.EncodingProfile)
*/
@Override
protected File getOutputFile(File source, EncodingProfile profile) {
String outputPath = PathSupport.removeFileExtension(source.getAbsolutePath()) + profile.getSuffix();
return new File(outputPath);
}
/**
* Callback from the engine controllers stating that encoding of the given file has been successful.
*
* @param file
* the track that was encoded
* @param profile
* the encoding profile
*/
void fileEncoded(File file, EncodingProfile profile) {
fireEncoded(this, profile, file);
}
/**
* Callback from the engine controllers stating that encoding of the given file has has failed for the specified
* reason.
*
* @param file
* the file that was encoded
* @param profile
* the encoding profile
* @param reason
* the reason of failure
*/
void fileEncodingFailed(File file, EncodingProfile profile, String reason) {
fireEncodingFailed(this, profile, new EncoderException(this, reason), file);
}
/**
* Callback from the engine controllers stating that encoding of the given file has progressed to the given value.
*
* @param file
* the file that is being encoded
* @param profile
* the encoding profile
* @param progress
*/
void fileEncodingProgressed(File file, EncodingProfile profile, int progress) {
fireEncodingProgressed(this, file, profile, progress);
}
/**
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "Telestream Episode Engine";
}
/*
* (non-Javadoc)
*
* @see org.opencastproject.composer.api.EncoderEngine#extract(java.io.File,
* org.opencastproject.composer.api.EncodingProfile, java.util.Map, long[])
*/
@Override
public List<File> parallelEncode(File mediaSource, EncodingProfile format, Map<String, String> properties) throws EncoderException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
}