diff --git a/webrtc/examples/androidapp/src/org/appspot/apprtc/PeerConnectionClient.java b/webrtc/examples/androidapp/src/org/appspot/apprtc/PeerConnectionClient.java index 72e9cac4d2..c9f880ee5e 100644 --- a/webrtc/examples/androidapp/src/org/appspot/apprtc/PeerConnectionClient.java +++ b/webrtc/examples/androidapp/src/org/appspot/apprtc/PeerConnectionClient.java @@ -17,8 +17,11 @@ import android.util.Log; import java.io.File; import java.io.IOException; import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; +import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Timer; @@ -972,60 +975,83 @@ public class PeerConnectionClient { return newSdpDescription.toString(); } - private static String preferCodec(String sdpDescription, String codec, boolean isAudio) { - String[] lines = sdpDescription.split("\r\n"); - int mLineIndex = -1; - String codecRtpMap = null; - // a=rtpmap: / [/] - String regex = "^a=rtpmap:(\\d+) " + codec + "(/\\d+)+[\r]?$"; - Pattern codecPattern = Pattern.compile(regex); - String mediaDescription = "m=video "; - if (isAudio) { - mediaDescription = "m=audio "; - } - for (int i = 0; (i < lines.length) && (mLineIndex == -1 || codecRtpMap == null); i++) { - if (lines[i].startsWith(mediaDescription)) { - mLineIndex = i; - continue; + /** Returns the line number containing "m=audio|video", or -1 if no such line exists. */ + private static int findMediaDescriptionLine(boolean isAudio, String[] sdpLines) { + final String mediaDescription = isAudio ? "m=audio " : "m=video "; + for (int i = 0; i < sdpLines.length; ++i) { + if (sdpLines[i].startsWith(mediaDescription)) { + return i; } + } + return -1; + } + + private static String joinString( + Iterable s, String delimiter, boolean delimiterAtEnd) { + Iterator iter = s.iterator(); + if (!iter.hasNext()) { + return ""; + } + StringBuilder buffer = new StringBuilder(iter.next()); + while (iter.hasNext()) { + buffer.append(delimiter).append(iter.next()); + } + if (delimiterAtEnd) { + buffer.append(delimiter); + } + return buffer.toString(); + } + + private static String movePayloadTypesToFront(List preferredPayloadTypes, String mLine) { + // The format of the media description line should be: m= ... + final List origLineParts = Arrays.asList(mLine.split(" ")); + if (origLineParts.size() <= 3) { + Log.e(TAG, "Wrong SDP media description format: " + mLine); + return null; + } + final List header = origLineParts.subList(0, 3); + final List unpreferredPayloadTypes = + new ArrayList(origLineParts.subList(3, origLineParts.size())); + unpreferredPayloadTypes.removeAll(preferredPayloadTypes); + // Reconstruct the line with |preferredPayloadTypes| moved to the beginning of the payload + // types. + final List newLineParts = new ArrayList(); + newLineParts.addAll(header); + newLineParts.addAll(preferredPayloadTypes); + newLineParts.addAll(unpreferredPayloadTypes); + return joinString(newLineParts, " ", false /* delimiterAtEnd */); + } + + private static String preferCodec(String sdpDescription, String codec, boolean isAudio) { + final String[] lines = sdpDescription.split("\r\n"); + final int mLineIndex = findMediaDescriptionLine(isAudio, lines); + if (mLineIndex == -1) { + Log.w(TAG, "No mediaDescription line, so can't prefer " + codec); + return sdpDescription; + } + // A list with all the payload types with name |codec|. The payload types are integers in the + // range 96-127, but they are stored as strings here. + final List codecPayloadTypes = new ArrayList(); + // a=rtpmap: / [/] + final Pattern codecPattern = Pattern.compile("^a=rtpmap:(\\d+) " + codec + "(/\\d+)+[\r]?$"); + for (int i = 0; i < lines.length; ++i) { Matcher codecMatcher = codecPattern.matcher(lines[i]); if (codecMatcher.matches()) { - codecRtpMap = codecMatcher.group(1); + codecPayloadTypes.add(codecMatcher.group(1)); } } - if (mLineIndex == -1) { - Log.w(TAG, "No " + mediaDescription + " line, so can't prefer " + codec); + if (codecPayloadTypes.isEmpty()) { + Log.w(TAG, "No payload types with name " + codec); return sdpDescription; } - if (codecRtpMap == null) { - Log.w(TAG, "No rtpmap for " + codec); + + final String newMLine = movePayloadTypesToFront(codecPayloadTypes, lines[mLineIndex]); + if (newMLine == null) { return sdpDescription; } - Log.d(TAG, "Found " + codec + " rtpmap " + codecRtpMap + ", prefer at " + lines[mLineIndex]); - String[] origMLineParts = lines[mLineIndex].split(" "); - if (origMLineParts.length > 3) { - StringBuilder newMLine = new StringBuilder(); - int origPartIndex = 0; - // Format is: m= ... - newMLine.append(origMLineParts[origPartIndex++]).append(" "); - newMLine.append(origMLineParts[origPartIndex++]).append(" "); - newMLine.append(origMLineParts[origPartIndex++]).append(" "); - newMLine.append(codecRtpMap); - for (; origPartIndex < origMLineParts.length; origPartIndex++) { - if (!origMLineParts[origPartIndex].equals(codecRtpMap)) { - newMLine.append(" ").append(origMLineParts[origPartIndex]); - } - } - lines[mLineIndex] = newMLine.toString(); - Log.d(TAG, "Change media description: " + lines[mLineIndex]); - } else { - Log.e(TAG, "Wrong SDP media description format: " + lines[mLineIndex]); - } - StringBuilder newSdpDescription = new StringBuilder(); - for (String line : lines) { - newSdpDescription.append(line).append("\r\n"); - } - return newSdpDescription.toString(); + Log.d(TAG, "Change media description from: " + lines[mLineIndex] + " to " + newMLine); + lines[mLineIndex] = newMLine; + return joinString(Arrays.asList(lines), "\r\n", true /* delimiterAtEnd */); } private void drainCandidates() {