All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.antmedia.whip.WhipEndpoint Maven / Gradle / Ivy

Go to download

Ant Media Server supports RTMP, RTSP, MP4, HLS, WebRTC, Adaptive Streaming, etc.

There is a newer version: 2.11.3
Show newest version
package io.antmedia.whip;

import java.net.URI;
import java.util.ArrayList;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;

import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.kafka.common.Uuid;
import org.json.simple.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import io.antmedia.datastore.db.types.Broadcast;
import io.antmedia.rest.RestServiceBase;
import io.antmedia.rest.model.Result;
import io.antmedia.webrtc.PublishParameters;
import io.antmedia.websocket.WebSocketConstants;
import io.swagger.v3.oas.annotations.ExternalDocumentation;
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.info.Contact;
import io.swagger.v3.oas.annotations.info.Info;
import io.swagger.v3.oas.annotations.info.License;
import io.swagger.v3.oas.annotations.servers.Server;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.HeaderParam;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.UriInfo;
import jakarta.ws.rs.core.Response.Status;


@OpenAPIDefinition(
		info = @Info(
				description = "Ant Media Server WHIP endpoint",
				version = "v2.0",
				title = "Ant Media Server WHIP Endpoint",
				contact = @Contact(name = "Ant Media Info", email = "[email protected]", url = "https://antmedia.io"),
				license = @License(name = "Apache 2.0", url = "https://www.apache.org/licenses/LICENSE-2.0")),
		externalDocs = @ExternalDocumentation(description = "Rest Guide", url="https://antmedia.io/docs")

		)
@Component
//we bind it to /whip path in web.xml
@Path("/")
public class WhipEndpoint extends RestServiceBase {

	private static final Logger logger = LoggerFactory.getLogger(WhipEndpoint.class);



	@Operation(summary = "Publish a webrtc stream through WebRTC-HTTP ingestion protocol(WHIP). HTTP for signaling.")
	@POST
	@Consumes({ "application/sdp" })
	@Path("/{streamId}")
	@Produces("application/sdp")
	public CompletableFuture startWhipPublish(@Context UriInfo uriInfo, @PathParam(WebSocketConstants.STREAM_ID) String streamId,
			@QueryParam(WebSocketConstants.VIDEO) Boolean enableVideo,
			@QueryParam(WebSocketConstants.AUDIO) Boolean enableAudio,
			@QueryParam(WebSocketConstants.SUBSCRIBER_ID) String subscriberId,
			@QueryParam(WebSocketConstants.SUBSCRIBER_CODE) String subscriberCode,
			@QueryParam(WebSocketConstants.STREAM_NAME) String streamName,
			@QueryParam(WebSocketConstants.MAIN_TRACK) String mainTrack,
			@QueryParam(WebSocketConstants.META_DATA) String metaData,
			@QueryParam(WebSocketConstants.LINK_SESSION) String linkedSession,
			@HeaderParam("Authorization") String token, 
			@Parameter String sdp) {

		PublishParameters publishParameters = new PublishParameters(streamId);
		
		String prefix = "Bearer ";
	    
	    if (token != null && token.toLowerCase().startsWith(prefix.toLowerCase())) {
	        // Extract the token by removing the prefix
	        token = token.substring(prefix.length());
	    }
		
		
		publishParameters.setToken(token);
		
		publishParameters.setEnableVideo(enableVideo == null || enableVideo);
		publishParameters.setEnableAudio(enableAudio == null || enableAudio);
		publishParameters.setSubscriberId(subscriberId);
		publishParameters.setSubscriberCode(subscriberCode);
		publishParameters.setStreamName(streamName);
		publishParameters.setMainTrack(mainTrack);
		publishParameters.setMetaData(metaData);

		
		String sessionId = UUID.randomUUID().toString();

		CompletableFuture startHttpSignaling = getApplication().startHttpSignaling(publishParameters, sdp, sessionId);

		return startHttpSignaling.exceptionally(e -> {
			logger.error("Could not complete webrtc http signaling: {}", ExceptionUtils.getStackTrace(e));
			
			return null;
		}).thenApply(result -> { return prepareResponse(result, sessionId, uriInfo); });

	}

	public Response prepareResponse(Result result, String sessionId, UriInfo uriInfo) {

		try {
			if(!result.isSuccess())
			{
				return Response.status(Status.FORBIDDEN)
						.entity(result.getMessage())
						.build();
			}


			//TODO: make it parametric
			String defaultStunStr = "stun:stun1.l.google.com:19302; rel=ice-server";
			ArrayList extensions = new ArrayList<>();
			extensions.add(defaultStunStr);
			
			
			String turnAddr = getApplication().getAppSettings().getStunServerURI();
			String turnServerUsername = getApplication().getAppSettings().getTurnServerUsername();
			String turnServerPassword = getApplication().getAppSettings().getTurnServerCredential();
			String turnServerInfo = "";

			if(StringUtils.isNotBlank(turnServerUsername) && StringUtils.isNotBlank(turnServerPassword)){
				//TODO: Increase security here
				turnServerInfo = turnAddr + "?transport=udp; rel=\"ice-server\" username="+turnServerUsername+";"+" credential="+turnServerPassword;
				extensions.add(turnServerInfo);
			}

			

			String eTag = sessionId; // Replace with parsed ETag
			String resource = uriInfo.getRequestUri().toString()+"/"+eTag;
			URI uri = URI.create(resource);

			return Response.created(uri)
					.status(Status.CREATED)
					.entity(result.getMessage())
					.header("ETag", eTag)
					.header("Link", String.join(",", extensions))
					.type("application/sdp")
					.build();
		}
		catch (Exception e) {
			//Complete future with error hides the exception so we need to explicitly log it and return it
			logger.error("Exception in prepareResponse {}", ExceptionUtils.getStackTrace(e));
			return Response.serverError().build();
		}

	}

	@Operation(summary = "Stop a webrtc stream through WebRTC-HTTP ingestion protocol(WHIP). HTTP for signaling.")
	@DELETE
	@Consumes({ "application/sdp" })
	@Path("/{streamId}/{eTag}")
	@Produces(MediaType.APPLICATION_JSON)
	public Response stopWhipPublish(@PathParam("streamId") String streamId, @PathParam("eTag") String eTag){

		Result result = getApplication().stopWhipBroadcast(streamId, eTag);
		if(result.isSuccess()){
			return Response.ok().entity(result).build();
		}
		return Response.status(Status.NOT_FOUND).entity(result).build();
	}


}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy