2
2
3
3
import android .util .Log ;
4
4
5
+ import androidx .annotation .Nullable ;
6
+ import androidx .annotation .StringDef ;
7
+
5
8
import org .webrtc .AudioTrack ;
6
9
import org .webrtc .DataChannel ;
7
10
import org .webrtc .IceCandidate ;
11
14
import org .webrtc .PeerConnectionFactory ;
12
15
import org .webrtc .RTCStatsReport ;
13
16
import org .webrtc .RtpReceiver ;
17
+ import org .webrtc .RtpSender ;
14
18
import org .webrtc .RtpTransceiver ;
15
19
import org .webrtc .SdpObserver ;
16
20
import org .webrtc .SessionDescription ;
17
21
import org .webrtc .VideoTrack ;
18
22
23
+ import java .lang .annotation .Retention ;
24
+ import java .lang .annotation .RetentionPolicy ;
19
25
import java .util .ArrayList ;
20
26
import java .util .Arrays ;
27
+ import java .util .Iterator ;
21
28
import java .util .List ;
22
29
import java .util .concurrent .ExecutorService ;
30
+ import java .util .regex .Matcher ;
31
+ import java .util .regex .Pattern ;
23
32
24
33
public class RTCPeer implements SdpObserver , PeerConnection .Observer {
25
34
private static final String TAG = "RTCPeer" ;
@@ -32,6 +41,23 @@ public class RTCPeer implements SdpObserver, PeerConnection.Observer {
32
41
private final PeerConnectionEvents mEvents ;
33
42
private boolean isError ;
34
43
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
+
35
61
public RTCPeer (PeerConnectionFactory factory , ExecutorService executor , PeerConnectionEvents events ) {
36
62
this .mFactory = factory ;
37
63
mExecutor = executor ;
@@ -68,9 +94,12 @@ public void addAudioTrack(AudioTrack audioTrack, List<String> mediaStreamLabels)
68
94
pc .addTrack (audioTrack , mediaStreamLabels );
69
95
}
70
96
71
- public void setRemoteDescription (SessionDescription sdp ) {
97
+ public void setRemoteDescription (SessionDescription desc ) {
72
98
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 );
74
103
}
75
104
76
105
public void addRemoteIceCandidate (final IceCandidate candidate ) {
@@ -95,13 +124,11 @@ public List<RtpTransceiver> getTransceivers() {
95
124
return pc .getTransceivers ();
96
125
}
97
126
98
-
99
127
public void dispose () {
100
128
if (pc == null ) return ;
101
129
pc .dispose ();
102
130
}
103
131
104
-
105
132
private MediaConstraints offerOrAnswerConstraint () {
106
133
MediaConstraints mediaConstraints = new MediaConstraints ();
107
134
ArrayList <MediaConstraints .KeyValuePair > keyValuePairs = new ArrayList <>();
@@ -126,7 +153,104 @@ public void getStats() {
126
153
pc .getStats (mEvents ::onPeerConnectionStatsReady );
127
154
}
128
155
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
+ }
129
217
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
+ }
130
254
131
255
// region ------------------------------SdpObserver------------------------
132
256
@ Override
@@ -136,6 +260,7 @@ public void onCreateSuccess(SessionDescription desc) {
136
260
return ;
137
261
}
138
262
String sdp = desc .description ;
263
+ sdp = preferCodec (sdp , videoCodecType , false );
139
264
final SessionDescription newDesc = new SessionDescription (desc .type , sdp );
140
265
localDescription = newDesc ;
141
266
mExecutor .execute (() -> {
@@ -276,7 +401,6 @@ public void onTrack(RtpTransceiver transceiver) {
276
401
}
277
402
278
403
279
-
280
404
// endregion
281
405
282
406
0 commit comments