package ch.rasc.wampspring.demo.webrtc; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Collection; import java.util.Collections; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import javax.imageio.ImageIO; import javax.xml.bind.DatatypeConverter; import org.imgscalr.Scalr; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.fasterxml.jackson.databind.ObjectMapper; import ch.rasc.wampspring.EventMessenger; import ch.rasc.wampspring.annotation.WampCallListener; import ch.rasc.wampspring.annotation.WampUnsubscribeListener; import ch.rasc.wampspring.message.CallMessage; import ch.rasc.wampspring.message.UnsubscribeMessage; import net.sf.uadetector.ReadableUserAgent; import net.sf.uadetector.UserAgentStringParser; import net.sf.uadetector.service.UADetectorServiceFactory; @Service public class ChatService { private static final String DATA_IMAGE = "data:image/png;base64,"; private final static Logger logger = LoggerFactory.getLogger(ChatService.class); private final static ObjectMapper mapper = new ObjectMapper(); private final UserAgentStringParser parser = UADetectorServiceFactory .getResourceModuleParser(); private final Map<String, UserConnection> socketIdToUserMap = new ConcurrentHashMap<>(); private final EventMessenger eventMessenger; @Autowired public ChatService(EventMessenger eventMessenger) { this.eventMessenger = eventMessenger; } @WampCallListener("readConnectedUsers") public Collection<UserConnection> readConnectedUsers() { return this.socketIdToUserMap.values(); } @WampCallListener("connect") public void connect(CallMessage callMessage, UserConnection newUser) { ReadableUserAgent ua = this.parser.parse(newUser.getBrowser()); if (ua != null) { newUser.setBrowser(ua.getName() + " " + ua.getVersionNumber().getMajor()); } newUser.setSessionId(callMessage.getWebSocketSessionId()); this.socketIdToUserMap.put(callMessage.getWebSocketSessionId(), newUser); this.eventMessenger.sendToAll("connected", newUser); } @WampUnsubscribeListener("message") public void unsubscribeClient(UnsubscribeMessage unsubscribeMessage) { UserConnection uc = this.socketIdToUserMap .remove(unsubscribeMessage.getWebSocketSessionId()); if (uc != null) { this.eventMessenger.sendToAll("disconnected", uc); } } @WampCallListener("hangup") public void hangup(String connectedWith) { String webSocketSessionId = findUserConnection(connectedWith); if (webSocketSessionId != null) { this.eventMessenger.sendTo("hangup", null, Collections.singleton(webSocketSessionId)); } } private String findUserConnection(String userName) { for (String webSocketSessionId : this.socketIdToUserMap.keySet()) { UserConnection uc = this.socketIdToUserMap.get(webSocketSessionId); if (uc.getUsername().equals(userName)) { return webSocketSessionId; } } return null; } @WampCallListener("snapshot") public void snapshot(CallMessage callMessage, String image) { UserConnection uc = this.socketIdToUserMap .get(callMessage.getWebSocketSessionId()); if (uc != null && image.startsWith(DATA_IMAGE)) { try { byte[] imageBytes = DatatypeConverter .parseBase64Binary(image.substring(DATA_IMAGE.length())); String resizedImageDataURL = resize(imageBytes); uc.setImage(resizedImageDataURL); this.eventMessenger.sendToAll("snapshot", uc); } catch (IOException e) { e.printStackTrace(); } } } private static String resize(byte[] imageData) throws IOException { try (ByteArrayInputStream bis = new ByteArrayInputStream(imageData); ByteArrayOutputStream bos = new ByteArrayOutputStream()) { BufferedImage image = ImageIO.read(bis); BufferedImage resizedImage = Scalr.resize(image, Scalr.Method.AUTOMATIC, Scalr.Mode.AUTOMATIC, 40, Scalr.OP_ANTIALIAS); ImageIO.write(resizedImage, "png", bos); return DATA_IMAGE + DatatypeConverter.printBase64Binary(bos.toByteArray()); } } @WampCallListener("sendSdp") public void sendSdp(Map<String, Object> offerObject) { String toUsername = (String) offerObject.get("toUsername"); String webSocketSessionId = findUserConnection(toUsername); if (webSocketSessionId != null) { this.eventMessenger.sendTo("receiveSdp", offerObject, Collections.singleton(webSocketSessionId)); } if (logger.isDebugEnabled()) { try { logger.debug(mapper.writerWithDefaultPrettyPrinter() .writeValueAsString(offerObject)); } catch (IOException e) { // ignore this } } } @WampCallListener("sendIceCandidate") public void sendIceCandidate(Map<String, Object> candidate) { String toUsername = (String) candidate.get("toUsername"); String webSocketSessionId = findUserConnection(toUsername); if (webSocketSessionId != null) { this.eventMessenger.sendTo("receiveIceCandidate", candidate, Collections.singleton(webSocketSessionId)); } if (logger.isDebugEnabled()) { try { logger.debug(mapper.writerWithDefaultPrettyPrinter() .writeValueAsString(candidate)); } catch (IOException e) { // ignore this } } } }