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}
This commit is contained in:
@ -17,8 +17,11 @@ import android.util.Log;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Timer;
|
import java.util.Timer;
|
||||||
@ -972,60 +975,83 @@ public class PeerConnectionClient {
|
|||||||
return newSdpDescription.toString();
|
return newSdpDescription.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String preferCodec(String sdpDescription, String codec, boolean isAudio) {
|
/** Returns the line number containing "m=audio|video", or -1 if no such line exists. */
|
||||||
String[] lines = sdpDescription.split("\r\n");
|
private static int findMediaDescriptionLine(boolean isAudio, String[] sdpLines) {
|
||||||
int mLineIndex = -1;
|
final String mediaDescription = isAudio ? "m=audio " : "m=video ";
|
||||||
String codecRtpMap = null;
|
for (int i = 0; i < sdpLines.length; ++i) {
|
||||||
// a=rtpmap:<payload type> <encoding name>/<clock rate> [/<encoding parameters>]
|
if (sdpLines[i].startsWith(mediaDescription)) {
|
||||||
String regex = "^a=rtpmap:(\\d+) " + codec + "(/\\d+)+[\r]?$";
|
return i;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String joinString(
|
||||||
|
Iterable<? extends CharSequence> s, String delimiter, boolean delimiterAtEnd) {
|
||||||
|
Iterator<? extends CharSequence> 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<String> preferredPayloadTypes, String mLine) {
|
||||||
|
// The format of the media description line should be: m=<media> <port> <proto> <fmt> ...
|
||||||
|
final List<String> origLineParts = Arrays.asList(mLine.split(" "));
|
||||||
|
if (origLineParts.size() <= 3) {
|
||||||
|
Log.e(TAG, "Wrong SDP media description format: " + mLine);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final List<String> header = origLineParts.subList(0, 3);
|
||||||
|
final List<String> unpreferredPayloadTypes =
|
||||||
|
new ArrayList<String>(origLineParts.subList(3, origLineParts.size()));
|
||||||
|
unpreferredPayloadTypes.removeAll(preferredPayloadTypes);
|
||||||
|
// Reconstruct the line with |preferredPayloadTypes| moved to the beginning of the payload
|
||||||
|
// types.
|
||||||
|
final List<String> newLineParts = new ArrayList<String>();
|
||||||
|
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<String> codecPayloadTypes = new ArrayList<String>();
|
||||||
|
// a=rtpmap:<payload type> <encoding name>/<clock rate> [/<encoding parameters>]
|
||||||
|
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]);
|
Matcher codecMatcher = codecPattern.matcher(lines[i]);
|
||||||
if (codecMatcher.matches()) {
|
if (codecMatcher.matches()) {
|
||||||
codecRtpMap = codecMatcher.group(1);
|
codecPayloadTypes.add(codecMatcher.group(1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (mLineIndex == -1) {
|
if (codecPayloadTypes.isEmpty()) {
|
||||||
Log.w(TAG, "No " + mediaDescription + " line, so can't prefer " + codec);
|
Log.w(TAG, "No payload types with name " + codec);
|
||||||
return sdpDescription;
|
return sdpDescription;
|
||||||
}
|
}
|
||||||
if (codecRtpMap == null) {
|
|
||||||
Log.w(TAG, "No rtpmap for " + codec);
|
final String newMLine = movePayloadTypesToFront(codecPayloadTypes, lines[mLineIndex]);
|
||||||
|
if (newMLine == null) {
|
||||||
return sdpDescription;
|
return sdpDescription;
|
||||||
}
|
}
|
||||||
Log.d(TAG, "Found " + codec + " rtpmap " + codecRtpMap + ", prefer at " + lines[mLineIndex]);
|
Log.d(TAG, "Change media description from: " + lines[mLineIndex] + " to " + newMLine);
|
||||||
String[] origMLineParts = lines[mLineIndex].split(" ");
|
lines[mLineIndex] = newMLine;
|
||||||
if (origMLineParts.length > 3) {
|
return joinString(Arrays.asList(lines), "\r\n", true /* delimiterAtEnd */);
|
||||||
StringBuilder newMLine = new StringBuilder();
|
|
||||||
int origPartIndex = 0;
|
|
||||||
// Format is: m=<media> <port> <proto> <fmt> ...
|
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drainCandidates() {
|
private void drainCandidates() {
|
||||||
|
|||||||
Reference in New Issue
Block a user