From 93f01be4fd463f98c08e99d87aa350fb5567ddbf Mon Sep 17 00:00:00 2001 From: magjed Date: Sun, 29 Jan 2017 15:14:17 -0800 Subject: [PATCH] Android AppRTCMobile: Fix SDP video codec reordering for multiple H264 profiles Since we can now have multiple H264 payload type, we need to move all of them to the beginning of the codec list, instead of greedily taking the first payload type that matches the preferred codec name. BUG=webrtc:6738 Review-Url: https://codereview.webrtc.org/2658573009 Cr-Commit-Position: refs/heads/master@{#16349} --- .../appspot/apprtc/PeerConnectionClient.java | 116 +++++++++++------- 1 file changed, 71 insertions(+), 45 deletions(-) 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() {