Java tutorial
/* * (C) Copyright 2016 NUBOMEDIA (http://www.nubomedia.eu) * * Licensed under the Apache 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://www.apache.org/licenses/LICENSE-2.0 * * 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 eu.nubomedia.tutorial.repository; import java.util.Collections; import java.util.Map; import org.kurento.client.EndOfStreamEvent; import org.kurento.client.ErrorEvent; import org.kurento.client.EventListener; import org.kurento.client.IceCandidate; import org.kurento.client.KurentoClient; import org.kurento.client.MediaPipeline; import org.kurento.client.MediaProfileSpecType; import org.kurento.client.OnIceCandidateEvent; import org.kurento.client.PlayerEndpoint; import org.kurento.client.RecorderEndpoint; import org.kurento.client.WebRtcEndpoint; import org.kurento.jsonrpc.JsonUtils; import org.kurento.repository.RepositoryClient; import org.kurento.repository.service.pojo.RepositoryItemPlayer; import org.kurento.repository.service.pojo.RepositoryItemRecorder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.socket.TextMessage; import org.springframework.web.socket.WebSocketSession; import com.google.gson.JsonObject; /** * User session. * * @author Boni Garcia (boni.garcia@urjc.es) * @since 6.4.1 */ public class UserSession { private static final int REPOSITORY_GUARD_TIME_MS = 10000; private final Logger log = LoggerFactory.getLogger(UserSession.class); private RepositoryHandler handler; private RepositoryClient repositoryClient; private KurentoClient kurentoClient; private MediaPipeline mediaPipeline; private WebRtcEndpoint webRtcEndpoint; private RecorderEndpoint recorderEndpoint; private PlayerEndpoint playerEndpoint; private RepositoryItemRecorder repositoryItemRecorder; private String sessionId; private long stopTimestamp; public UserSession(String sessionId, RepositoryClient repositoryClient, RepositoryHandler handler) { this.sessionId = sessionId; this.repositoryClient = repositoryClient; this.handler = handler; } public String startRecording(WebSocketSession session, String sdpOffer) { // KurentoClient kurentoClient = KurentoClient.create(); log.info("Created kurentoClient (session {})", sessionId); // Media pipeline mediaPipeline = kurentoClient.createMediaPipeline(); log.info("Created Media Pipeline for recording {} (session {})", mediaPipeline.getId(), sessionId); // Repository item (recorder) try { Map<String, String> metadata = Collections.emptyMap(); repositoryItemRecorder = repositoryClient.createRepositoryItem(metadata); } catch (Exception e) { log.warn("Exception creating repositoryItemRecorder", e); // This code allows to run the demo in local without a repository server repositoryItemRecorder = new RepositoryItemRecorder(); String id = String.valueOf(System.currentTimeMillis()); repositoryItemRecorder.setId(id); repositoryItemRecorder.setUrl("file:///tmp/" + id + ".webm"); } log.info("Repository item id={}, url={}", repositoryItemRecorder.getId(), repositoryItemRecorder.getUrl()); // Media elements and connectivity webRtcEndpoint = new WebRtcEndpoint.Builder(mediaPipeline).build(); recorderEndpoint = new RecorderEndpoint.Builder(mediaPipeline, repositoryItemRecorder.getUrl()) .withMediaProfile(MediaProfileSpecType.WEBM).build(); webRtcEndpoint.connect(webRtcEndpoint); webRtcEndpoint.connect(recorderEndpoint); // WebRTC negotiation String sdpAnswer = performWebRtcNegotiation(session, sdpOffer); // Start recording recorderEndpoint.record(); return sdpAnswer; } public String playRecording(final WebSocketSession session, String sdpOffer) { // KurentoClient kurentoClient = KurentoClient.create(); log.info("Created kurentoClient (session {})", sessionId); // Media pipeline mediaPipeline = kurentoClient.createMediaPipeline(); log.info("Created Media Pipeline for playing {} (session {})", mediaPipeline.getId(), sessionId); // Repository item (player) RepositoryItemPlayer repositoryItemPlayer = null; try { // Guard time for repository to finish storing media long diff = System.currentTimeMillis() - stopTimestamp; if (diff >= 0 && diff < REPOSITORY_GUARD_TIME_MS) { log.info("Waiting for {}ms before requesting the repository read endpoint " + "(requires {}ms before upload is considered terminated " + "and only {}ms have passed)", REPOSITORY_GUARD_TIME_MS - diff, REPOSITORY_GUARD_TIME_MS, diff); Thread.sleep(REPOSITORY_GUARD_TIME_MS - diff); } repositoryItemPlayer = repositoryClient.getReadEndpoint(repositoryItemRecorder.getId()); } catch (Exception e) { log.warn("Exception creating repositoryItemPlayer", e); // This code allows to run the demo in local without a repository server repositoryItemPlayer = new RepositoryItemPlayer(); repositoryItemPlayer.setId(repositoryItemRecorder.getId()); repositoryItemPlayer.setUrl(repositoryItemRecorder.getUrl()); } // Media elements and connectivity webRtcEndpoint = new WebRtcEndpoint.Builder(mediaPipeline).build(); playerEndpoint = new PlayerEndpoint.Builder(mediaPipeline, repositoryItemPlayer.getUrl()).build(); playerEndpoint.connect(webRtcEndpoint); playerEndpoint.addErrorListener(new EventListener<ErrorEvent>() { @Override public void onEvent(ErrorEvent event) { log.info("ErrorEvent for session {}: {}", session.getId(), event.getDescription()); handler.sendPlayEnd(session); release(); } }); playerEndpoint.addEndOfStreamListener(new EventListener<EndOfStreamEvent>() { @Override public void onEvent(EndOfStreamEvent event) { log.info("EndOfStreamEvent for session {}", session.getId()); handler.sendPlayEnd(session); release(); } }); // WebRTC negotiation String sdpAnswer = performWebRtcNegotiation(session, sdpOffer); // Start playing playerEndpoint.play(); return sdpAnswer; } public String performWebRtcNegotiation(final WebSocketSession session, String sdpOffer) { log.info("Starting WebRTC negotiation in session {}", sessionId); // Subscribe to ICE candidates webRtcEndpoint.addOnIceCandidateListener(new EventListener<OnIceCandidateEvent>() { @Override public void onEvent(OnIceCandidateEvent event) { JsonObject response = new JsonObject(); response.addProperty("id", "iceCandidate"); response.add("candidate", JsonUtils.toJsonObject(event.getCandidate())); handler.sendMessage(session, new TextMessage(response.toString())); } }); // SDP negotiation String sdpAnswer = webRtcEndpoint.processOffer(sdpOffer); // Gather ICE candidates webRtcEndpoint.gatherCandidates(); return sdpAnswer; } public void stopRecording() { recorderEndpoint.stop(); stopTimestamp = System.currentTimeMillis(); release(); } public void addCandidate(JsonObject jsonCandidate) { IceCandidate candidate = new IceCandidate(jsonCandidate.get("candidate").getAsString(), jsonCandidate.get("sdpMid").getAsString(), jsonCandidate.get("sdpMLineIndex").getAsInt()); webRtcEndpoint.addIceCandidate(candidate); } public void stopPlay() { playerEndpoint.stop(); release(); } public void release() { log.info("Releasing media pipeline {} (session {})", mediaPipeline.getId(), sessionId); mediaPipeline.release(); log.info("Destroying kurentoClient (session {})", sessionId); kurentoClient.destroy(); } }