package net.pms.network;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.ByteArrayInputStream;
import java.net.InetAddress;
import java.net.URL;
import java.util.*;
import java.util.Map.Entry;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import net.pms.PMS;
import static net.pms.network.UPNPHelper.sleep;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import net.pms.dlna.protocolinfo.DeviceProtocolInfo;
import net.pms.dlna.protocolinfo.PanasonicDmpProfiles;
import net.pms.util.BasicPlayer;
import net.pms.util.StringUtil;
import org.apache.commons.lang.StringUtils;
import org.fourthline.cling.DefaultUpnpServiceConfiguration;
import org.fourthline.cling.UpnpService;
import org.fourthline.cling.UpnpServiceImpl;
import org.fourthline.cling.controlpoint.ActionCallback;
import org.fourthline.cling.controlpoint.SubscriptionCallback;
import org.fourthline.cling.model.ServerClientTokens;
import org.fourthline.cling.model.action.*;
import org.fourthline.cling.model.gena.*;
import org.fourthline.cling.model.message.UpnpHeaders;
import org.fourthline.cling.model.message.UpnpResponse;
import org.fourthline.cling.model.message.header.DeviceTypeHeader;
import org.fourthline.cling.model.message.header.UpnpHeader;
import org.fourthline.cling.model.meta.*;
import org.fourthline.cling.model.types.DeviceType;
import org.fourthline.cling.model.types.ServiceId;
import org.fourthline.cling.model.types.UDADeviceType;
import org.fourthline.cling.model.types.UDN;
import org.fourthline.cling.registry.DefaultRegistryListener;
import org.fourthline.cling.registry.Registry;
import org.fourthline.cling.registry.RegistryListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class UPNPControl {
// Logger ids to write messages to the logs.
private static final Logger LOGGER = LoggerFactory.getLogger(UPNPControl.class);
public static final DeviceType[] mediaRendererTypes = new DeviceType[] {
new UDADeviceType("MediaRenderer", 1),
// Older Sony Blurays provide only 'Basic' service
new UDADeviceType("Basic", 1)
};
private static UpnpService upnpService;
private static UpnpHeaders UMSHeaders;
private static DocumentBuilder db;
public static final int ACTIVE = 0;
public static final int CONTROLS = 1;
public static final int RENEW = 2;
public static final int AVT = BasicPlayer.PLAYCONTROL;
public static final int RC = BasicPlayer.VOLUMECONTROL;
public static final int ANY = 0xff;
private static final boolean DEBUG = true; // log upnp state vars
protected static Map<String, Renderer> socketMap = new HashMap<>();
public static class DeviceMap<T extends Renderer> extends HashMap<String, HashMap<String, T>> {
private static final long serialVersionUID = 1510675619549915489L;
private Class<T> TClass;
public DeviceMap(Class<T> t) {
TClass = t;
}
public T get(String uuid, String id) {
if (!containsKey(uuid)) {
put(uuid, new HashMap<String, T>());
}
HashMap<String, T> m = get(uuid);
if (!m.containsKey(id)) {
try {
T newitem = TClass.newInstance();
newitem.uuid = uuid;
m.put(id, newitem);
} catch (Exception e) {
LOGGER.error("Error instantiating item {}[{}]: {}", uuid, id, e.getMessage());
LOGGER.trace("", e);
}
}
return m.get(id);
}
public String get(String uuid, String id, String key) {
return get(uuid, id).data.get(key);
}
public boolean containsKey(String uuid, String id) {
return containsKey(uuid) && get(uuid).containsKey(id);
}
public HashMap<String, String> getData(String uuid, String id) {
if (containsKey(uuid, id)) {
return get(uuid, id).data;
}
return null;
}
public T put(String uuid, String id, T item) {
item.uuid = uuid;
if (!containsKey(uuid)) {
get(uuid, "0");
}
return get(uuid).put(id, item);
}
public String put(String uuid, String id, String key, String value) {
return get(uuid, id).data.put(key, value);
}
public void mark(String uuid, int property, Object value) {
HashMap<String, T> m = get(uuid);
if (m != null) {
for (T i : m.values()) {
switch (property) {
case ACTIVE:
i.setActive((boolean) value);
break;
case RENEW:
i.renew = (boolean) value;
break;
case CONTROLS:
i.controls = (int) value;
break;
default:
break;
}
}
}
}
}
protected static DeviceMap rendererMap;
public static class Renderer {
public int controls;
protected ActionEvent event;
public String uuid;
public String instanceID = "0"; // FIXME: unclear in what precise context a media renderer's instanceID != 0
public final HashMap<String, String> data;
public Map<String, String> details;
public LinkedHashSet<ActionListener> listeners;
private Thread monitor;
public volatile boolean active, renew;
public final DeviceProtocolInfo deviceProtocolInfo = new DeviceProtocolInfo();
public volatile PanasonicDmpProfiles panasonicDmpProfiles;
public Renderer(String uuid) {
this();
this.uuid = uuid;
}
public Renderer() {
controls = 0;
active = false;
data = new HashMap<>();
details = null;
listeners = new LinkedHashSet<>();
event = new ActionEvent(this, 0, null);
monitor = null;
renew = false;
data.put("TransportState", "STOPPED");
}
public void alert() {
if (isUpnpDevice(uuid) && (monitor == null || !monitor.isAlive()) && !"STOPPED".equals(data.get("TransportState"))) {
monitor();
}
for (ActionListener l : listeners) {
l.actionPerformed(event);
}
}
public Map<String, String> connect(ActionListener listener) {
listeners.add(listener);
return data;
}
public void disconnect(ActionListener listener) {
listeners.remove(listener);
}
public void monitor() {
final Device d = getDevice(uuid);
monitor = new Thread(new Runnable() {
@Override
public void run() {
String id = data.get("InstanceID");
while (active && !"STOPPED".equals(data.get("TransportState"))) {
sleep(1000);
// if (DEBUG) LOGGER.debug("InstanceID: " + id);
for (ActionArgumentValue o : getPositionInfo(d, id)) {
data.put(o.getArgument().getName(), o.toString());
// if (DEBUG) LOGGER.debug(o.getArgument().getName() + ": " + o.toString());
}
alert();
}
if (!active) {
data.put("TransportState", "STOPPED");
alert();
}
}
}, "UPNP-" + d.getDetails().getFriendlyName());
monitor.start();
}
public boolean hasPlayControls() {
return (controls & BasicPlayer.PLAYCONTROL) != 0;
}
public boolean hasVolumeControls() {
return (controls & BasicPlayer.VOLUMECONTROL) != 0;
}
public boolean isActive() {
return active;
}
public void setActive(boolean b) {
active = b;
}
public boolean needsRenewal() {
return !active || renew;
}
}
public static Device getDevice(String uuid) {
return uuid != null && upnpService != null ? upnpService.getRegistry().getDevice(UDN.valueOf(uuid), false) : null;
}
public static synchronized void xml2d(String uuid, String xml, Renderer item) {
try {
Document doc = db.parse(new ByteArrayInputStream(xml.getBytes()));
// doc.getDocumentElement().normalize();
NodeList ids = doc.getElementsByTagName("InstanceID");
for (int i = 0; i < ids.getLength(); i++) {
NodeList c = ids.item(i).getChildNodes();
String id = ((Element) ids.item(i)).getAttribute("val");
// if (DEBUG) LOGGER.debug("InstanceID: " + id);
if (item == null) {
item = rendererMap.get(uuid, id);
}
item.data.put("InstanceID", id);
for (int n = 0; n < c.getLength(); n++) {
if (c.item(n).getNodeType() != Node.ELEMENT_NODE) {
// LOGGER.debug("skip this " + c.item(n));
continue;
}
Element e = (Element) c.item(n);
String name = e.getTagName();
String val = e.getAttribute("val");
if (DEBUG) {
LOGGER.debug(name + ": " + val);
}
item.data.put(name, val);
}
item.alert();
}
} catch (Exception e) {
LOGGER.debug("Error parsing xml: " + e);
}
}
public UPNPControl() {
rendererMap = new DeviceMap<>(Renderer.class);
}
public void init() {
try {
db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
UMSHeaders = new UpnpHeaders();
UMSHeaders.add(UpnpHeader.Type.USER_AGENT.getHttpName(), "UMS/" + PMS.getVersion() + " " + new ServerClientTokens());
DefaultUpnpServiceConfiguration sc = new DefaultUpnpServiceConfiguration() {
@Override
public UpnpHeaders getDescriptorRetrievalHeaders(RemoteDeviceIdentity identity) {
return UMSHeaders;
}
};
RegistryListener rl = new DefaultRegistryListener() {
@Override
public void remoteDeviceAdded(Registry registry, RemoteDevice device) {
super.remoteDeviceAdded(registry, device);
if (isBlocked(getUUID(device)) || !addRenderer(device)) {
LOGGER.trace("Ignoring remote device: {} {}", device.getType().getType(), device);
}
// This may be unnecessary, but we might as well be thorough
if (device.hasEmbeddedDevices()) {
for (Device<?, RemoteDevice, ?> embedded : device.getEmbeddedDevices()) {
if (isBlocked(getUUID(embedded)) || !addRenderer(embedded)) {
LOGGER.debug("Ignoring embedded device: {} {}", embedded.getType(), embedded.toString());
}
}
}
}
@Override
public void remoteDeviceRemoved(Registry registry, RemoteDevice d) {
super.remoteDeviceRemoved(registry, d);
String uuid = getUUID(d);
if (rendererMap.containsKey(uuid)) {
rendererMap.mark(uuid, ACTIVE, false);
rendererRemoved(d);
}
}
@Override
public void remoteDeviceUpdated(Registry registry, RemoteDevice d) {
super.remoteDeviceUpdated(registry, d);
rendererUpdated(d);
}
};
upnpService = new UpnpServiceImpl(sc, rl);
for (DeviceType t : mediaRendererTypes) {
upnpService.getControlPoint().search(new DeviceTypeHeader(t));
}
LOGGER.debug("UPNP Services are online, listening for media renderers");
} catch (Exception ex) {
LOGGER.debug("UPNP startup Error", ex);
}
}
public void shutdown() {
new Thread(new Runnable() {
@Override
public void run() {
if (upnpService != null) {
LOGGER.debug("Stopping UPNP Services...");
upnpService.shutdown();
}
}
}).start();
}
public static boolean isMediaRenderer(Device d) {
String t = d.getType().getType();
for (DeviceType r : mediaRendererTypes) {
if (r.getType().equals(t)) {
return true;
}
}
return false;
}
public static boolean isUpnpDevice(String uuid) {
return getDevice(uuid) != null;
}
public static boolean isActive(String uuid, String id) {
if (rendererMap.containsKey(uuid, id)) {
return rendererMap.get(uuid, id).active;
}
return false;
}
public static boolean isUpnpControllable(String uuid) {
if (rendererMap.containsKey(uuid)) {
return rendererMap.get(uuid, "0").controls != 0;
}
return false;
}
public static String getFriendlyName(String uuid) {
return getFriendlyName(getDevice(uuid));
}
public static String getFriendlyName(Device d) {
return d != null ? d.getDetails().getFriendlyName() : null;
}
public static String getUUID(Device d) {
return d.getIdentity().getUdn().toString();
}
public static URL getURL(Device d) {
return d instanceof RemoteDevice ? ((RemoteDevice) d).getIdentity().getDescriptorURL() :
d.getDetails().getBaseURL();
}
public static List<String> getServiceNames(Device d) {
ArrayList<String> services = new ArrayList<>();
for (Service s : d.getServices()) {
services.add(s.getServiceId().getId());
}
return services;
}
public static Map<String, String> getDeviceDetails(Device d) {
if (d == null) {
return null;
}
DeviceDetails dev = d.getDetails();
ManufacturerDetails man = dev.getManufacturerDetails();
ModelDetails model = dev.getModelDetails();
LinkedHashMap<String, String> details = new LinkedHashMap<>();
details.put("friendlyName", dev.getFriendlyName());
details.put("address", getURL(d).getHost());
details.put("udn", getUUID(d));
Object detail;
if ((detail = man.getManufacturer()) != null) {
details.put("manufacturer", (String) detail);
}
if ((detail = model.getModelName()) != null) {
details.put("modelName", (String) detail);
}
if ((detail = model.getModelNumber()) != null) {
details.put("modelNumber", (String) detail);
}
if ((detail = model.getModelDescription()) != null) {
details.put("modelDescription", (String) detail);
}
if ((detail = man.getManufacturerURI()) != null) {
details.put("manufacturerURL", detail.toString());
}
if ((detail = model.getModelURI()) != null) {
details.put("modelURL", detail.toString());
}
return details;
}
public static String getDeviceDetailsString(Device d) {
return StringUtils.join(getDeviceDetails(d).values(), " ");
}
public static String getDeviceIcon(Renderer r, int maxHeight) {
if (isUpnpDevice(r.uuid)) {
return getDeviceIcon(getDevice(r.uuid), maxHeight);
}
return null;
}
public static String getDeviceIcon(Device d, int maxHeight) {
URL base = getURL(d);
Icon icon = null;
String url = null;
int maxH = maxHeight == 0 ? 99999 : maxHeight, height = 0;
for (Icon i : d.getIcons()) {
int h = i.getHeight();
if (h < maxH && h > height) {
icon = i;
height = h;
}
}
try {
url = icon != null ? new URL(base, icon.getUri().toString()).toString() : null;
} catch (Exception e) {}
LOGGER.debug("Device icon: " + url);
return url;
}
protected boolean addRenderer(String uuid) {
Device device = getDevice(uuid);
return device != null ? addRenderer(device) : false;
}
protected synchronized boolean addRenderer(Device<?, RemoteDevice, ?> device) {
if (device != null) {
String uuid = getUUID(device);
if (isMediaRenderer(device) && rendererFound(device, uuid) != null) {
LOGGER.debug("Adding device: {} {}", device.getType(), device.toString());
subscribeAll(device, uuid);
getProtocolInfo(device);
rendererMap.mark(uuid, ACTIVE, true);
rendererReady(uuid);
return true;
}
}
return false;
}
protected void subscribeAll(Device d, String uuid) {
String name = getFriendlyName(d);
int ctrl = 0;
for (Service s : d.getServices()) {
String sid = s.getServiceId().getId();
LOGGER.debug("Subscribing to " + sid + " service on " + name);
if (sid.contains("AVTransport")) {
ctrl |= AVT;
} else if (sid.contains("RenderingControl")) {
ctrl |= RC;
}
upnpService.getControlPoint().execute(new SubscriptionCB(s));
}
rendererMap.mark(uuid, RENEW, false);
rendererMap.mark(uuid, CONTROLS, ctrl);
}
protected Renderer rendererFound(Device d, String uuid) {
// Create an instance
return rendererMap.get(uuid, "0");
}
protected void rendererReady(String uuid) {
}
protected boolean isBlocked(String uuid) {
return false;
}
protected void rendererUpdated(Device d) {
String uuid = getUUID(d);
if (rendererMap.containsKey(uuid)) {
if (rendererMap.get(uuid, "0").needsRenewal()) {
LOGGER.debug("Renewing subscriptions to ", getFriendlyName(d));
subscribeAll(d, uuid);
}
rendererMap.mark(uuid, ACTIVE, true);
}
}
protected void rendererRemoved(Device d) {
LOGGER.debug(getFriendlyName(d) + " is now offline.");
}
public static String getUUID(String addr) {
try {
return getUUID(InetAddress.getByName(addr));
} catch (Exception e) {
return null;
}
}
public static String getUUID(InetAddress socket) {
Device d = getDevice(socket);
if (d != null) {
return getUUID(d);
}
return null;
}
// Returns the first device regardless of type at the given address, if any
public static Device getAnyDevice(InetAddress socket) {
if (upnpService != null) {
for (Device d : upnpService.getRegistry().getDevices()) {
try {
InetAddress devsocket = InetAddress.getByName(getURL(d).getHost());
if (devsocket.equals(socket)) {
return d;
}
} catch (Exception e) {}
}
}
return null;
}
// Returns the first renderer at the given address, if any
public static Device getDevice(InetAddress socket) {
if (upnpService != null) {
for (DeviceType r : mediaRendererTypes) {
for (Device d : upnpService.getRegistry().getDevices(r)) {
try {
InetAddress devsocket = InetAddress.getByName(getURL(d).getHost());
if (devsocket.equals(socket)) {
return d;
}
} catch (Exception e) {}
}
}
}
return null;
}
public static Renderer getRenderer(String uuid) {
if (rendererMap.containsKey(uuid)) {
return rendererMap.get(uuid, "0");
}
return null;
}
public static boolean isNonRenderer(InetAddress socket) {
Device d = getDevice(socket);
boolean b = (d != null && !isMediaRenderer(d));
if (b) {
LOGGER.debug("Device at {} is {}: {}", socket, d.getType(), d.toString());
}
return b;
}
public static void connect(String uuid, String instanceID, ActionListener listener) {
rendererMap.get(uuid, instanceID).connect(listener);
}
public static Map<String, String> getData(String uuid, String instanceID) {
return rendererMap.get(uuid, instanceID).data;
}
public UpnpService getService() {
return upnpService;
}
public static class SubscriptionCB extends SubscriptionCallback {
private String uuid;
public SubscriptionCB(Service s) {
super(s);
uuid = getUUID(s.getDevice());
}
@Override
public void eventReceived(GENASubscription subscription) {
rendererMap.mark(uuid, ACTIVE, true);
if (subscription.getCurrentValues().containsKey("LastChange")) {
xml2d(uuid, subscription.getCurrentValues().get("LastChange").toString(), null);
}
}
@Override
public void established(GENASubscription sub) {
LOGGER.debug("Subscription established: " + sub.getService().getServiceId().getId() +
" on " + getFriendlyName(uuid));
}
@Override
public void failed(GENASubscription sub, UpnpResponse response, Exception ex, String defaultMsg) {
LOGGER.debug("Subscription failed: " + sub.getService().getServiceId().getId() +
" on " + getFriendlyName(uuid) + ": " + defaultMsg.split(": ", 2)[1]);
}
@Override
public void failed(GENASubscription sub, UpnpResponse response, Exception ex) {
LOGGER.debug("Subscription failed: " + sub.getService().getServiceId().getId() +
" on " + getFriendlyName(uuid) + ": " + createDefaultFailureMessage(response, ex).split(": ", 2)[1]);
}
@Override
public void ended(GENASubscription sub, CancelReason reason, UpnpResponse response) {
// Reason should be null, or it didn't end regularly
if (reason != null) {
LOGGER.debug("Subscription cancelled: " + sub.getService().getServiceId().getId() +
" on " + uuid + ": " + reason);
}
rendererMap.mark(uuid, RENEW, true);
}
@Override
public void eventsMissed(GENASubscription sub, int numberOfMissedEvents) {
LOGGER.debug("Missed events: " + numberOfMissedEvents + " for subscription " + sub.getService().getServiceId().getId() + " on " + getFriendlyName(uuid));
}
}
// Convenience functions for sending various upnp service requests
public static ActionInvocation send(final Device dev, String instanceID, String service, String action, String... args) {
Service svc = dev.findService(ServiceId.valueOf("urn:upnp-org:serviceId:" + service));
final String uuid = getUUID(dev);
if (svc != null) {
Action x = svc.getAction(action);
String name = getFriendlyName(dev);
boolean log = !action.equals("GetPositionInfo");
if (x != null) {
ActionInvocation a = new ActionInvocation(x);
a.setInput("InstanceID", instanceID);
for (int i = 0; i < args.length; i += 2) {
a.setInput(args[i], args[i + 1]);
}
if (log) {
LOGGER.debug("Sending upnp {}.{} {} to {}[{}]", service, action, args, name, instanceID);
}
new ActionCallback(a, upnpService.getControlPoint()) {
@Override
public void success(ActionInvocation invocation) {
rendererMap.mark(uuid, ACTIVE, true);
}
@Override
public void failure(ActionInvocation invocation, UpnpResponse operation, String defaultMsg) {
LOGGER.debug("Action failed: {}", defaultMsg);
rendererMap.mark(uuid, ACTIVE, false);
}
}.run();
if (log) {
for (ActionArgumentValue arg : a.getOutput()) {
LOGGER.debug("Received from {}[{}]: {}={}", name, instanceID, arg.getArgument().getName(), arg.toString());
}
}
return a;
}
}
return null;
}
// ConnectionManager
public static void getProtocolInfo(Device<?, RemoteDevice, ?> device) {
Service<RemoteDevice, RemoteService> connectionManager = device.findService(ServiceId.valueOf("ConnectionManager"));
if (connectionManager != null) {
Action<RemoteService> action = connectionManager.getAction("GetProtocolInfo");
final String name = getFriendlyName(device);
if (action != null) {
final String uuid = getUUID(device);
ActionInvocation<RemoteService> actionInvocation = new ActionInvocation<>(action);
new ActionCallback(actionInvocation, upnpService.getControlPoint()) {
@Override
public void success(ActionInvocation invocation) {
String sink = invocation.getOutput("Sink").toString();
rendererMap.get(uuid, "0").deviceProtocolInfo.add(DeviceProtocolInfo.GET_PROTOCOLINFO_SINK, sink);
if (LOGGER.isTraceEnabled()) {
StringBuilder sb = new StringBuilder();
for (Object element : invocation.getOutputMap().entrySet()) {
@SuppressWarnings("unchecked")
Entry<String, ActionArgumentValue<?>> entry = (Entry<String, ActionArgumentValue<?>>) element;
if (entry.getValue() != null) {
String value = entry.getValue().toString();
if (isNotBlank(value)) {
sb.append("\n").append(entry.getKey()).append(":\n ");
sb.append(value.replaceAll(",", "\n "));
}
}
}
if (sb.length() > 0) {
LOGGER.trace("Received GetProtocolInfo from \"{}\": {}", name, sb.toString());
} else {
LOGGER.trace("Received empty reply to GetProtocolInfo from \"{}\"", name);
}
}
}
@Override
public void failure(ActionInvocation invocation, UpnpResponse operation, String defaultMsg) {
LOGGER.debug(
"GetProtocolInfo from \"{}\" failed with status code {}: {} ({})",
name,
operation.getStatusCode(),
operation.getStatusMessage(),
defaultMsg
);
}
}.run();
}
}
}
// AVTransport
// Play modes
public final static String NORMAL = "NORMAL";
public final static String REPEAT_ONE = "REPEAT_ONE";
public final static String REPEAT_ALL = "REPEAT_ALL";
public final static String RANDOM = "RANDOM";
// Seek modes
public final static String REL_BYTE = "X_DLNA_REL_BYTE";
public final static String REL_TIME = "REL_TIME";
public final static String TRACK_NR = "TRACK_NR";
public static void play(Device dev, String instanceID) {
send(dev, instanceID, "AVTransport", "Play", "Speed", "1");
}
public static void pause(Device dev, String instanceID) {
send(dev, instanceID, "AVTransport", "Pause");
}
public static void next(Device dev, String instanceID) {
send(dev, instanceID, "AVTransport", "Next");
}
public static void previous(Device dev, String instanceID) {
send(dev, instanceID, "AVTransport", "Previous");
}
public static void seek(Device dev, String instanceID, String mode, String target) {
// REL_TIME target format is "hh:mm:ss"
send(dev, instanceID, "AVTransport", "Seek", "Unit", mode, "Target", target);
}
public static void stop(Device dev, String instanceID) {
send(dev, instanceID, "AVTransport", "Stop");
}
public static String getCurrentTransportState(Device dev, String instanceID) {
return send(dev, instanceID, "AVTransport", "GetTransportInfo")
.getOutput("CurrentTransportState").toString();
}
public static String getCurrentTransportActions(Device dev, String instanceID) {
return send(dev, instanceID, "AVTransport", "GetCurrentTransportActions")
.getOutput("CurrentTransportActions").toString();
}
public static String getDeviceCapabilities(Device dev, String instanceID) {
return send(dev, instanceID, "AVTransport", "GetDeviceCapabilities")
.getOutput("DeviceCapabilities").toString();
}
public static String getMediaInfo(Device dev, String instanceID) {
return send(dev, instanceID, "AVTransport", "GetMediaInfo")
.getOutput("MediaInfo").toString();
}
public static ActionArgumentValue[] getPositionInfo(Device dev, String instanceID) {
return send(dev, instanceID, "AVTransport", "GetPositionInfo").getOutput();
}
public static String getTransportInfo(Device dev, String instanceID) {
return send(dev, instanceID, "AVTransport", "GetTransportInfo")
.getOutput("TransportInfo").toString();
}
public static String getTransportSettings(Device dev, String instanceID) {
return send(dev, instanceID, "AVTransport", "GetTransportSettings")
.getOutput("TransportSettings").toString();
}
public static void setAVTransportURI(Device dev, String instanceID, String uri, String metaData) {
send(dev, instanceID, "AVTransport", "SetAVTransportURI", "CurrentURI", uri,
"CurrentURIMetaData", metaData != null ? StringUtil.unEncodeXML(metaData) : null);
}
public static void setPlayMode(Device dev, String instanceID, String mode) {
send(dev, instanceID, "AVTransport", "SetPlayMode", "NewPlayMode", mode);
}
public static String X_DLNA_GetBytePositionInfo(Device dev, String instanceID, String trackSize) {
return send(dev, instanceID, "AVTransport", "X_DLNA_GetBytePositionInfo", "TrackSize", trackSize)
.getOutput("BytePositionInfo").toString();
}
// RenderingControl
// Audio channels
public final static String MASTER = "Master";
public final static String LF = "LF";
public final static String RF = "RF";
public static String getMute(Device dev, String instanceID) {
return getMute(dev, instanceID, MASTER);
}
public static String getMute(Device dev, String instanceID, String channel) {
return send(dev, instanceID, "RenderingControl", "GetMute", "Channel", channel)
.getOutput("Mute").toString();
}
public static String getVolume(Device dev, String instanceID) {
return getVolume(dev, instanceID, MASTER);
}
public static String getVolume(Device dev, String instanceID, String channel) {
return send(dev, instanceID, "RenderingControl", "GetVolume", "Channel", channel)
.getOutput("Volume").toString();
}
public static void setMute(Device dev, String instanceID, boolean on) {
setMute(dev, instanceID, on, MASTER);
}
public static void setMute(Device dev, String instanceID, boolean on, String channel) {
send(dev, instanceID, "RenderingControl", "SetMute", "DesiredMute", on ? "1" : "0", "Channel", channel);
}
public static void setVolume(Device dev, String instanceID, int volume) {
setVolume(dev, instanceID, volume, MASTER);
}
public static void setVolume(Device dev, String instanceID, int volume, String channel) {
// volume = 1 to 100
send(dev, instanceID, "RenderingControl", "SetVolume", "DesiredVolume", String.valueOf(volume), "Channel", channel);
}
}