Skip to content

Commit 3f7c1d6

Browse files
committed
daily commit
1 parent 830864f commit 3f7c1d6

File tree

4 files changed

+175
-9
lines changed

4 files changed

+175
-9
lines changed

temple1/src/main/java/com/dds/temple1/ConnectActivity.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ public void onConnectedToRoom(AppRTCClient.SignalingParameters params) {
191191
runOnUiThread(() -> {
192192
boolean initiator = params.initiator;
193193
mRtcEngine.createPeerConnection(this, remoteProxyRenderer);
194+
mRtcEngine.setVideoCodecType(RTCPeer.VIDEO_CODEC_H264);
194195
if (initiator) {
195196
// create offer
196197
mMainHandler.post(() -> logAndToast("create offer"));
@@ -315,6 +316,7 @@ public void onConnected() {
315316
runOnUiThread(() -> {
316317
logAndToast("DTLS connected, delay=" + delta + "ms");
317318
mRtcEngine.enableStatsEvents(true, 1000);
319+
mRtcEngine.setBitrateRange(500, 2000);
318320
});
319321

320322
}
@@ -355,9 +357,7 @@ private void logAndToast(String msg) {
355357
private static int getSystemUiVisibility() {
356358
int flags = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN |
357359
View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
358-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
359-
flags |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
360-
}
360+
flags |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
361361
return flags;
362362
}
363363

temple1/src/main/java/com/dds/temple1/rtc/RTCEngine.java

+42-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
import org.webrtc.AudioSource;
1313
import org.webrtc.AudioTrack;
1414
import org.webrtc.Camera1Enumerator;
15-
import org.webrtc.Camera2Enumerator;
1615
import org.webrtc.CameraEnumerator;
1716
import org.webrtc.CameraVideoCapturer;
1817
import org.webrtc.DefaultVideoDecoderFactory;
@@ -22,6 +21,8 @@
2221
import org.webrtc.MediaConstraints;
2322
import org.webrtc.MediaStreamTrack;
2423
import org.webrtc.PeerConnectionFactory;
24+
import org.webrtc.RtpParameters;
25+
import org.webrtc.RtpSender;
2526
import org.webrtc.RtpTransceiver;
2627
import org.webrtc.SessionDescription;
2728
import org.webrtc.SurfaceTextureHelper;
@@ -75,6 +76,9 @@ public class RTCEngine {
7576
private static final String DISABLE_WEBRTC_AGC_FIELDTRIAL =
7677
"WebRTC-Audio-MinimizeResamplingOnMobile/Enabled/";
7778

79+
private static final int BPS_IN_KBPS = 1000;
80+
81+
7882
private static final ExecutorService executor = Executors.newSingleThreadExecutor();
7983

8084
public RTCEngine(Context context, EglBase eglBase, VideoSink localSink) {
@@ -345,6 +349,43 @@ public void toggleBeautyEffect() {
345349
});
346350
}
347351

352+
public void setBitrateRange(int minBitrate, int maxBitrate) {
353+
executor.execute(() -> {
354+
if (mPeer != null) {
355+
if (minBitrate > maxBitrate) {
356+
Log.w(TAG, "minBitrate must < maxBitrate.");
357+
return;
358+
}
359+
RtpSender videoSender = mPeer.findVideoSender();
360+
if (videoSender == null) {
361+
Log.w(TAG, "RtpSender are not ready.");
362+
return;
363+
}
364+
RtpParameters parameters = videoSender.getParameters();
365+
if (parameters.encodings.size() == 0) {
366+
Log.w(TAG, "RtpParameters are not ready.");
367+
return;
368+
}
369+
for (RtpParameters.Encoding encoding : parameters.encodings) {
370+
// Null value means no limit.
371+
encoding.maxBitrateBps = maxBitrate == 0 ? null : maxBitrate * BPS_IN_KBPS;
372+
encoding.minBitrateBps = Math.max(minBitrate, 300) * BPS_IN_KBPS;
373+
}
374+
}
375+
});
376+
377+
378+
}
379+
380+
public void setVideoCodecType(@RTCPeer.VideoCodeType String videoCodecType) {
381+
executor.execute(() -> {
382+
if (mPeer != null) {
383+
mPeer.setVideoCodecType(videoCodecType);
384+
}
385+
});
386+
387+
}
388+
348389
private final Timer statsTimer = new Timer();
349390

350391
public void enableStatsEvents(boolean enable, int periodMs) {

temple1/src/main/java/com/dds/temple1/rtc/RTCPeer.java

+129-5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
import android.util.Log;
44

5+
import androidx.annotation.Nullable;
6+
import androidx.annotation.StringDef;
7+
58
import org.webrtc.AudioTrack;
69
import org.webrtc.DataChannel;
710
import org.webrtc.IceCandidate;
@@ -11,15 +14,21 @@
1114
import org.webrtc.PeerConnectionFactory;
1215
import org.webrtc.RTCStatsReport;
1316
import org.webrtc.RtpReceiver;
17+
import org.webrtc.RtpSender;
1418
import org.webrtc.RtpTransceiver;
1519
import org.webrtc.SdpObserver;
1620
import org.webrtc.SessionDescription;
1721
import org.webrtc.VideoTrack;
1822

23+
import java.lang.annotation.Retention;
24+
import java.lang.annotation.RetentionPolicy;
1925
import java.util.ArrayList;
2026
import java.util.Arrays;
27+
import java.util.Iterator;
2128
import java.util.List;
2229
import java.util.concurrent.ExecutorService;
30+
import java.util.regex.Matcher;
31+
import java.util.regex.Pattern;
2332

2433
public class RTCPeer implements SdpObserver, PeerConnection.Observer {
2534
private static final String TAG = "RTCPeer";
@@ -32,6 +41,23 @@ public class RTCPeer implements SdpObserver, PeerConnection.Observer {
3241
private final PeerConnectionEvents mEvents;
3342
private boolean isError;
3443

44+
private static final String VIDEO_TRACK_TYPE = "video";
45+
public static final String VIDEO_CODEC_VP8 = "VP8";
46+
public static final String VIDEO_CODEC_VP9 = "VP9";
47+
public static final String VIDEO_CODEC_H264 = "H264";
48+
public static final String VIDEO_CODEC_H264_BASELINE = "H264 Baseline";
49+
public static final String VIDEO_CODEC_H264_HIGH = "H264 High";
50+
public static final String VIDEO_CODEC_AV1 = "AV1";
51+
52+
@StringDef({VIDEO_CODEC_VP8, VIDEO_CODEC_VP9, VIDEO_CODEC_H264, VIDEO_CODEC_H264_BASELINE, VIDEO_CODEC_H264_HIGH, VIDEO_CODEC_AV1})
53+
@Retention(RetentionPolicy.SOURCE)
54+
@interface VideoCodeType {
55+
56+
}
57+
58+
private String videoCodecType = VIDEO_CODEC_VP8;
59+
60+
3561
public RTCPeer(PeerConnectionFactory factory, ExecutorService executor, PeerConnectionEvents events) {
3662
this.mFactory = factory;
3763
mExecutor = executor;
@@ -68,9 +94,12 @@ public void addAudioTrack(AudioTrack audioTrack, List<String> mediaStreamLabels)
6894
pc.addTrack(audioTrack, mediaStreamLabels);
6995
}
7096

71-
public void setRemoteDescription(SessionDescription sdp) {
97+
public void setRemoteDescription(SessionDescription desc) {
7298
if (pc == null) return;
73-
pc.setRemoteDescription(this, sdp);
99+
String sdp = desc.description;
100+
sdp = preferCodec(sdp, videoCodecType, false);
101+
SessionDescription sdpRemote = new SessionDescription(desc.type, sdp);
102+
pc.setRemoteDescription(this, sdpRemote);
74103
}
75104

76105
public void addRemoteIceCandidate(final IceCandidate candidate) {
@@ -95,13 +124,11 @@ public List<RtpTransceiver> getTransceivers() {
95124
return pc.getTransceivers();
96125
}
97126

98-
99127
public void dispose() {
100128
if (pc == null) return;
101129
pc.dispose();
102130
}
103131

104-
105132
private MediaConstraints offerOrAnswerConstraint() {
106133
MediaConstraints mediaConstraints = new MediaConstraints();
107134
ArrayList<MediaConstraints.KeyValuePair> keyValuePairs = new ArrayList<>();
@@ -126,7 +153,104 @@ public void getStats() {
126153
pc.getStats(mEvents::onPeerConnectionStatsReady);
127154
}
128155

156+
public RtpSender findVideoSender() {
157+
for (RtpSender sender : pc.getSenders()) {
158+
if (sender.track() != null) {
159+
String trackType = sender.track().kind();
160+
if (trackType.equals(VIDEO_TRACK_TYPE)) {
161+
Log.d(TAG, "Found video sender.");
162+
return sender;
163+
}
164+
}
165+
}
166+
return null;
167+
}
168+
169+
public void setVideoCodecType(@VideoCodeType String videoCodecType) {
170+
this.videoCodecType = videoCodecType;
171+
}
172+
173+
private static String preferCodec(String sdp, String codec, boolean isAudio) {
174+
final String[] lines = sdp.split("\r\n");
175+
final int mLineIndex = findMediaDescriptionLine(isAudio, lines);
176+
if (mLineIndex == -1) {
177+
Log.w(TAG, "No mediaDescription line, so can't prefer " + codec);
178+
return sdp;
179+
}
180+
// A list with all the payload types with name `codec`. The payload types are integers in the
181+
// range 96-127, but they are stored as strings here.
182+
final List<String> codecPayloadTypes = new ArrayList<>();
183+
// a=rtpmap:<payload type> <encoding name>/<clock rate> [/<encoding parameters>]
184+
final Pattern codecPattern = Pattern.compile("^a=rtpmap:(\\d+) " + codec + "(/\\d+)+[\r]?$");
185+
for (String line : lines) {
186+
Matcher codecMatcher = codecPattern.matcher(line);
187+
if (codecMatcher.matches()) {
188+
codecPayloadTypes.add(codecMatcher.group(1));
189+
}
190+
}
191+
if (codecPayloadTypes.isEmpty()) {
192+
Log.w(TAG, "No payload types with name " + codec);
193+
return sdp;
194+
}
195+
196+
final String newMLine = movePayloadTypesToFront(codecPayloadTypes, lines[mLineIndex]);
197+
if (newMLine == null) {
198+
return sdp;
199+
}
200+
Log.d(TAG, "Change media description from: " + lines[mLineIndex] + " to " + newMLine);
201+
lines[mLineIndex] = newMLine;
202+
return joinString(Arrays.asList(lines), "\r\n", true /* delimiterAtEnd */);
203+
}
204+
205+
/**
206+
* Returns the line number containing "m=audio|video", or -1 if no such line exists.
207+
*/
208+
private static int findMediaDescriptionLine(boolean isAudio, String[] sdpLines) {
209+
final String mediaDescription = isAudio ? "m=audio " : "m=video ";
210+
for (int i = 0; i < sdpLines.length; ++i) {
211+
if (sdpLines[i].startsWith(mediaDescription)) {
212+
return i;
213+
}
214+
}
215+
return -1;
216+
}
129217

218+
private static @Nullable String movePayloadTypesToFront(
219+
List<String> preferredPayloadTypes, String mLine) {
220+
// The format of the media description line should be: m=<media> <port> <proto> <fmt> ...
221+
final List<String> origLineParts = Arrays.asList(mLine.split(" "));
222+
if (origLineParts.size() <= 3) {
223+
Log.e(TAG, "Wrong SDP media description format: " + mLine);
224+
return null;
225+
}
226+
final List<String> header = origLineParts.subList(0, 3);
227+
final List<String> unpreferredPayloadTypes =
228+
new ArrayList<>(origLineParts.subList(3, origLineParts.size()));
229+
unpreferredPayloadTypes.removeAll(preferredPayloadTypes);
230+
// Reconstruct the line with `preferredPayloadTypes` moved to the beginning of the payload
231+
// types.
232+
final List<String> newLineParts = new ArrayList<>();
233+
newLineParts.addAll(header);
234+
newLineParts.addAll(preferredPayloadTypes);
235+
newLineParts.addAll(unpreferredPayloadTypes);
236+
return joinString(newLineParts, " ", false /* delimiterAtEnd */);
237+
}
238+
239+
private static String joinString(
240+
Iterable<? extends CharSequence> s, String delimiter, boolean delimiterAtEnd) {
241+
Iterator<? extends CharSequence> iter = s.iterator();
242+
if (!iter.hasNext()) {
243+
return "";
244+
}
245+
StringBuilder buffer = new StringBuilder(iter.next());
246+
while (iter.hasNext()) {
247+
buffer.append(delimiter).append(iter.next());
248+
}
249+
if (delimiterAtEnd) {
250+
buffer.append(delimiter);
251+
}
252+
return buffer.toString();
253+
}
130254

131255
// region ------------------------------SdpObserver------------------------
132256
@Override
@@ -136,6 +260,7 @@ public void onCreateSuccess(SessionDescription desc) {
136260
return;
137261
}
138262
String sdp = desc.description;
263+
sdp = preferCodec(sdp, videoCodecType, false);
139264
final SessionDescription newDesc = new SessionDescription(desc.type, sdp);
140265
localDescription = newDesc;
141266
mExecutor.execute(() -> {
@@ -276,7 +401,6 @@ public void onTrack(RtpTransceiver transceiver) {
276401
}
277402

278403

279-
280404
// endregion
281405

282406

temple1/src/main/res/layout/activity_connect2.xml

+1
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
android:id="@+id/callStats"
6464
android:layout_width="160dp"
6565
android:layout_height="wrap_content"
66+
android:layout_marginTop="20dp"
6667
android:layout_marginStart="5dp"
6768
android:layout_marginBottom="80dp"
6869
android:visibility="visible"

0 commit comments

Comments
 (0)