diff --git a/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java b/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java index f49db5ab6..250e04e8a 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java +++ b/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java @@ -61,6 +61,7 @@ import com.nextcloud.talk.events.SessionDescriptionSendEvent; import com.nextcloud.talk.persistence.entities.UserEntity; import com.nextcloud.talk.webrtc.MagicAudioManager; import com.nextcloud.talk.webrtc.MagicPeerConnectionWrapper; +import com.nextcloud.talk.webrtc.MagicWebRTCUtils; import org.apache.commons.lang3.StringEscapeUtils; import org.greenrobot.eventbus.EventBus; @@ -502,11 +503,17 @@ public class CallActivity extends AppCompatActivity { switch (type) { case "offer": case "answer": - Log.d("MARIO GOT ", type + " " + ncSignalingMessage.getFrom()); magicPeerConnectionWrapper.setNick(ncSignalingMessage.getPayload().getNick()); + String sessionDescriptionStringWithPreferredCodec = MagicWebRTCUtils.preferCodec + (ncSignalingMessage.getPayload().getSdp(), + "VP8", false); + + SessionDescription sessionDescriptionWithPreferredCodec = new SessionDescription( + SessionDescription.Type.fromCanonicalForm(type), + sessionDescriptionStringWithPreferredCodec); + magicPeerConnectionWrapper.getPeerConnection().setRemoteDescription(magicPeerConnectionWrapper - .getMagicSdpObserver(), new SessionDescription(SessionDescription.Type.fromCanonicalForm(type), - ncSignalingMessage.getPayload().getSdp())); + .getMagicSdpObserver(), sessionDescriptionWithPreferredCodec); break; case "candidate": NCIceCandidate ncIceCandidate = ncSignalingMessage.getPayload().getIceCandidate(); diff --git a/app/src/main/java/com/nextcloud/talk/webrtc/MagicPeerConnectionWrapper.java b/app/src/main/java/com/nextcloud/talk/webrtc/MagicPeerConnectionWrapper.java index 60c07ad8a..cd5696cf2 100644 --- a/app/src/main/java/com/nextcloud/talk/webrtc/MagicPeerConnectionWrapper.java +++ b/app/src/main/java/com/nextcloud/talk/webrtc/MagicPeerConnectionWrapper.java @@ -16,6 +16,7 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . + * */ package com.nextcloud.talk.webrtc; @@ -272,9 +273,17 @@ public class MagicPeerConnectionWrapper { @Override public void onCreateSuccess(SessionDescription sessionDescription) { - EventBus.getDefault().post(new SessionDescriptionSendEvent(sessionDescription, sessionId, + String sessionDescriptionStringWithPreferredCodec = MagicWebRTCUtils.preferCodec + (sessionDescription.description, + "VP8", false); + + SessionDescription sessionDescriptionWithPreferredCodec = new SessionDescription( + sessionDescription.type, + sessionDescriptionStringWithPreferredCodec); + + EventBus.getDefault().post(new SessionDescriptionSendEvent(sessionDescriptionWithPreferredCodec, sessionId, sessionDescription.type.canonicalForm().toLowerCase(), null)); - peerConnection.setLocalDescription(magicSdpObserver, sessionDescription); + peerConnection.setLocalDescription(magicSdpObserver, sessionDescriptionWithPreferredCodec); } @Override @@ -290,4 +299,5 @@ public class MagicPeerConnectionWrapper { } } } + } diff --git a/app/src/main/java/com/nextcloud/talk/webrtc/MagicWebRTCUtils.java b/app/src/main/java/com/nextcloud/talk/webrtc/MagicWebRTCUtils.java new file mode 100644 index 000000000..8ce5276c6 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/webrtc/MagicWebRTCUtils.java @@ -0,0 +1,125 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * Copyright (C) 2017 Mario Danic + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Original code: + * + * + * Copyright 2016 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +package com.nextcloud.talk.webrtc; + +import android.util.Log; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class MagicWebRTCUtils { + private static final String TAG = "MagicWebRTCUtils"; + + public 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()) { + codecPayloadTypes.add(codecMatcher.group(1)); + } + } + if (codecPayloadTypes.isEmpty()) { + Log.w(TAG, "No payload types with name " + codec); + return sdpDescription; + } + + final String newMLine = movePayloadTypesToFront(codecPayloadTypes, lines[mLineIndex]); + if (newMLine == null) { + return sdpDescription; + } + Log.d(TAG, "Change media description from: " + lines[mLineIndex] + " to " + newMLine); + lines[mLineIndex] = newMLine; + return joinString(Arrays.asList(lines), "\r\n", true /* delimiterAtEnd */); + } + + /** 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 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 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(); + } + +}