Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/guide/client-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -598,7 +598,7 @@ Whep.Session viewer = Whep.from(URI.create("https://example.com/live/whep"))
viewer.peer().onTrack(track -> System.out.println("Receiving " + track.kind()));
```

The offer goes to the endpoint with the local candidates, as WHIP and WHEP servers expect, and the future completes once the peer is connected. If the endpoint refuses the offer, or the peer does not connect within the timeout (15 seconds by default), the future fails, and the session is deleted on the server again. An endpoint that is busy, with status 503, or a WHEP endpoint whose stream has yet to start, with status 409, gets the offer again after the wait its `Retry-After` names, at least a second, as long as that fits within the timeout; without `Retry-After` the session fails at once. Cancelling the future, say once `get` gave up waiting, ends the session the same way. `onTrack` hands over tracks that already arrived, so a handler added once the future completed misses none.
The offer goes to the endpoint with the local candidates, as WHIP and WHEP servers expect, and the future completes once the peer is connected. If the endpoint refuses the offer, or the peer does not connect within the timeout (15 seconds by default), the future fails, and the session is deleted on the server again. An endpoint that is busy, with status 503, or a WHEP endpoint whose stream has yet to start, with status 409, gets the offer again after the wait its `Retry-After` names, at least a second, as long as that fits within the timeout; without `Retry-After` the session fails at once. A WHEP endpoint may also reject the offer and counter it with one of its own, with status 406: `Whep` then takes its offer back, answers the endpoint's, receiving only, and sends the answer by `PATCH` to the session the endpoint named, before the counter-offer's `valid-until`, 30 seconds by default. The answer rejects the audio or video of the counter-offer that `receive(...)` leaves out, and the session fails if the counter-offer holds none of what it asks for. Data channels of a counter-offer are accepted. Cancelling the future, say once `get` gave up waiting, ends the session the same way. `onTrack` hands over tracks that already arrived, so a handler added once the future completed misses none.

The timeout bounds the requests as a whole, not each read, so a server that sends its answer a byte at a time cannot stretch it. A request still out when the session times out or is cancelled is cut off at once; one still waiting for the head of its answer only from Java 16 on, and before that at its timeout. Once the server has named the session, in the head of its answer, it is deleted all the same; a session the server created for an answer that had not come yet is left for the server to time out.

Expand Down
8 changes: 8 additions & 0 deletions webrtc-jni/src/main/cpp/include/JNI_RTCRtpTransceiver.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions webrtc-jni/src/main/cpp/src/JNI_RTCRtpTransceiver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,17 @@ JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_RTCRtpTransceiver_stop
}
}

JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_RTCRtpTransceiver_stopImmediately
(JNIEnv * env, jobject caller)
{
webrtc::RtpTransceiverInterface * transceiver = GetHandle<webrtc::RtpTransceiverInterface>(env, caller);
CHECK_HANDLE(transceiver);

// Unlike StopStandard, stopped() is true at once, so that an answer
// created next rejects the m-line rather than making it inactive.
transceiver->StopInternal();
}

JNIEXPORT jboolean JNICALL Java_dev_onvoid_webrtc_RTCRtpTransceiver_stopped
(JNIEnv * env, jobject caller)
{
Expand Down
15 changes: 13 additions & 2 deletions webrtc/src/main/java/dev/onvoid/webrtc/RTCRtpTransceiver.java
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,22 @@ private RTCRtpTransceiver() {
* will no longer send, the receiver will no longer receive. Calling stop()
* updates the negotiation-needed flag for the RTCRtpTransceiver's
* associated RTCPeerConnection. Stopping a transceiver will cause future
* calls to createOffer or createAnswer to generate a zero port in the media
* description for the corresponding transceiver.
* calls to createOffer to generate a zero port in the media description
* for the corresponding transceiver. An answer created before the stop
* has been negotiated keeps the media description, as inactive; {@link
* #stopImmediately()} rejects it instead.
*/
public native void stop();

/**
* Irreversibly stops the RTCRtpTransceiver at once, without waiting for
* the stop to be negotiated, as {@link #stop()} does. An answer created
* after it rejects the media description of the transceiver, with a zero
* port, which lets an answerer decline a media description of a remote
* offer. This goes beyond the W3C API.
*/
public native void stopImmediately();

/**
* Indicates that the sender of this transceiver will no longer send, and
* that the receiver will no longer receive. It is true if either stop has
Expand Down
19 changes: 18 additions & 1 deletion webrtc/src/main/java/dev/onvoid/webrtc/client/HttpCall.java
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ final class HttpCall {

private Set<Integer> retryStatuses = Collections.emptySet();

/** Statuses other than 2xx whose body is read as that of a 2xx one. */
private Set<Integer> answerStatuses = Collections.emptySet();

/** The exchange under way, which an abort cancels. */
private CompletableFuture<?> exchange;

Expand Down Expand Up @@ -184,6 +187,20 @@ HttpCall retry(ScheduledExecutorService timer, Set<Integer> statuses) {
return this;
}

/**
* Reads the body of a response with one of the given statuses as that of
* a 2xx one, up to the size {@link #send} is given, rather than only as
* much as a message takes.
*
* @param statuses the statuses whose bodies are read in full.
*
* @return this request.
*/
HttpCall readBodies(Set<Integer> statuses) {
this.answerStatuses = statuses;
return this;
}

/**
* Follows redirects with status 307 and 308, up to {@link #MAX_REDIRECTS}
* of them, with the bearer token. One from https to plain http fails.
Expand Down Expand Up @@ -341,7 +358,7 @@ private HttpResponse.BodySubscriber<String> head(HttpResponse.ResponseInfo info,
Consumer<Response> onHead) {
int status = info.statusCode();
boolean redirect = isRedirect(status, info.headers().firstValue("Location").orElse(null));
boolean success = status / 100 == 2;
boolean success = status / 100 == 2 || answerStatuses.contains(status);
// A redirect's body is not read, and neither is an error's beyond what
// a message takes.
Body body = new Body(redirect ? 0 : success ? maxBody : MAX_ERROR_BODY, !success || redirect);
Expand Down
Loading
Loading