Skip to content

Commit 74e16a1

Browse files
committed
feat: add guide for configuring send-only and receive-only media
1 parent 52b7241 commit 74e16a1

File tree

3 files changed

+142
-0
lines changed

3 files changed

+142
-0
lines changed

docs/_sidebar.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
- [Data Channels](guide/data_channels.md)
1717
- [DTMF Sender](guide/dtmf_sender.md)
1818
- [RTC Stats](guide/rtc_stats.md)
19+
- [Send-only and Receive-only](guide/send_receive_direction.md)
1920
- [Logging](guide/logging.md)
2021

2122
- [**Build Notes**](build.md)

docs/guide/overview.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ This section provides detailed guides for various features of the webrtc-java li
1313
- [Desktop Capture](guide/desktop_capture.md) - Capturing and sharing screens and windows
1414
- [Custom Video Source](guide/custom_video_source.md) - Using custom video sources with WebRTC
1515
- [DTMF Sender](guide/dtmf_sender.md) - Sending DTMF tones in a call
16+
- [Send-only and Receive-only](guide/send_receive_direction.md) - Configure transceiver directions (send-only, receive-only or inactive)
1617

1718
## Data Communication
1819

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
# Send-only and Receive-only Media
2+
3+
This guide explains how to configure WebRTC media to be receive-only or send-only using the webrtc-java API. It also shows the equivalent configuration on the JavaScript (browser) side. You will learn how to:
4+
5+
- Create receive-only audio/video transceivers
6+
- Create send-only audio/video transceivers
7+
- Change direction at runtime
8+
- Understand the underlying SDP attributes (a=sendonly / a=recvonly / a=inactive)
9+
10+
The examples build upon the RTCRtpTransceiver API, which is the recommended way to control directionality in modern WebRTC.
11+
12+
Related API:
13+
- dev.onvoid.webrtc.RTCRtpTransceiver
14+
- dev.onvoid.webrtc.RTCRtpTransceiverInit
15+
- dev.onvoid.webrtc.RTCRtpTransceiverDirection
16+
17+
References in the repository:
18+
- Tests: [RTCPeerConnectionTests.java](https://github.com/devopvoid/webrtc-java/blob/main/webrtc/src/test/java/dev/onvoid/webrtc/RTCPeerConnectionTests.java)
19+
- Example (receive-only with WHEP): [WhepExample.java](https://github.com/devopvoid/webrtc-java/blob/main/webrtc-examples/src/main/java/dev/onvoid/webrtc/examples/WhepExample.java)
20+
21+
## Concepts overview
22+
23+
- SEND_RECV: Both sending and receiving are active (default when you add a track).
24+
- SEND_ONLY: Only sending is negotiated; you won’t receive media on this transceiver.
25+
- RECV_ONLY: Only receiving is negotiated; you won’t send media on this transceiver.
26+
- INACTIVE: Neither sending nor receiving on this transceiver.
27+
28+
These map to the SDP attributes a=sendrecv, a=sendonly, a=recvonly, a=inactive.
29+
30+
## Receive-only example
31+
32+
Use a transceiver with direction RECV_ONLY to indicate that you only want to receive media for a given kind (audio or video). You can optionally pass a dummy local track or omit sending entirely by not attaching a sending track.
33+
34+
```java
35+
import dev.onvoid.webrtc.*;
36+
import dev.onvoid.webrtc.media.video.VideoDeviceSource;
37+
import dev.onvoid.webrtc.media.video.VideoTrack;
38+
39+
PeerConnectionFactory factory = new PeerConnectionFactory();
40+
RTCConfiguration config = new RTCConfiguration();
41+
RTCPeerConnection pc = factory.createPeerConnection(config, candidate -> {});
42+
43+
// Create a video track/source (can be a dummy source when only receiving)
44+
VideoDeviceSource videoSource = new VideoDeviceSource();
45+
VideoTrack videoTrack = factory.createVideoTrack("videoTrack", videoSource);
46+
47+
// Configure transceiver as RECV_ONLY
48+
RTCRtpTransceiverInit init = new RTCRtpTransceiverInit();
49+
init.direction = RTCRtpTransceiverDirection.RECV_ONLY;
50+
RTCRtpTransceiver transceiver = pc.addTransceiver(videoTrack, init);
51+
52+
// Access the receiving track and attach a sink
53+
MediaStreamTrack track = transceiver.getReceiver().getTrack();
54+
if (track instanceof dev.onvoid.webrtc.media.video.VideoTrack vTrack) {
55+
vTrack.addSink(frame -> {
56+
// Handle incoming frames
57+
System.out.println("Received frame: " + frame);
58+
frame.release();
59+
});
60+
}
61+
```
62+
63+
Notes:
64+
- This pattern is used in the WhepExample included in the repository.
65+
- When you create the offer, the SDP will contain a=recvonly for that m= section.
66+
67+
## Send-only example
68+
69+
To send-only, set the transceiver direction to SEND_ONLY and provide a local track to send.
70+
71+
```java
72+
import dev.onvoid.webrtc.*;
73+
import dev.onvoid.webrtc.media.audio.AudioOptions;
74+
import dev.onvoid.webrtc.media.audio.AudioTrack;
75+
import dev.onvoid.webrtc.media.audio.AudioTrackSource;
76+
77+
PeerConnectionFactory factory = new PeerConnectionFactory();
78+
RTCPeerConnection pc = factory.createPeerConnection(new RTCConfiguration(), candidate -> {});
79+
80+
// Create an audio track to send
81+
AudioTrackSource audioSource = factory.createAudioSource(new AudioOptions());
82+
AudioTrack audioTrack = factory.createAudioTrack("audioTrack", audioSource);
83+
84+
// Configure transceiver as SEND_ONLY
85+
RTCRtpTransceiverInit init = new RTCRtpTransceiverInit();
86+
init.direction = RTCRtpTransceiverDirection.SEND_ONLY;
87+
RTCRtpTransceiver transceiver = pc.addTransceiver(audioTrack, init);
88+
89+
// Optionally verify
90+
assert transceiver.getDirection() == RTCRtpTransceiverDirection.SEND_ONLY;
91+
```
92+
93+
When you create the offer with this setup, the SDP will include a=sendonly for the audio m= section.
94+
95+
## Changing direction at runtime
96+
97+
You can change the direction dynamically. Remember that direction changes typically require renegotiation (createOffer/setLocalDescription -> signal -> setRemoteDescription).
98+
99+
```java
100+
RTCRtpTransceiver transceiver = /* previously created */;
101+
102+
// Change to INACTIVE
103+
transceiver.setDirection(RTCRtpTransceiverDirection.INACTIVE);
104+
105+
// Later switch to SEND_RECV
106+
transceiver.setDirection(RTCRtpTransceiverDirection.SEND_RECV);
107+
108+
// After changing directions, create a new offer and perform negotiation.
109+
RTCOfferOptions opts = new RTCOfferOptions();
110+
pc.createOffer(opts, new CreateSessionDescriptionObserver() {
111+
@Override
112+
public void onSuccess(RTCSessionDescription description) {
113+
pc.setLocalDescription(description, /* observer */ null);
114+
// Send to remote and await/set remote answer accordingly
115+
}
116+
@Override
117+
public void onFailure(String error) { /* handle error */ }
118+
});
119+
```
120+
121+
> Tip: You can also control sending without renegotiation by replacing the sender’s track or disabling it via MediaStreamTrack.setEnabled(false). However, the negotiated direction in SDP remains the same until you renegotiate.
122+
123+
## Common patterns and tips
124+
125+
- If you only need to receive a stream from a server (e.g., WHEP), use RECV_ONLY and avoid capturing local devices. This simplifies permissions and reduces CPU usage.
126+
- To temporarily stop sending without renegotiation, you can disable the sender’s track: sender.getTrack().setEnabled(false).
127+
- Use INACTIVE when neither sending nor receiving should occur on a transceiver, but you want to keep it for future use.
128+
- Direction changes typically require a new offer/answer exchange.
129+
130+
## Troubleshooting
131+
132+
- No remote media arriving in RECV_ONLY mode:
133+
- Ensure the remote endpoint actually sends media on that m= section.
134+
- Verify codecs overlap (see CodecListExample in examples).
135+
- Check network/firewall and ICE connectivity.
136+
- Permissions prompts appear even in receive-only mode:
137+
- Avoid creating real capture devices if you don’t need to send. You can add a transceiver with a dummy track.
138+
- SDP direction not as expected:
139+
- Confirm the transceiver direction before creating the offer.
140+
- Some changes only apply after renegotiation.

0 commit comments

Comments
 (0)