make jingle->sdp parsing fail on some obvious errors
This commit is contained in:
parent
ca9b95fc9c
commit
0bf991d95c
|
@ -214,14 +214,26 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
|
||||||
if (rtpContentMap == null) {
|
if (rtpContentMap == null) {
|
||||||
throw new IllegalStateException("initiator RTP Content Map has not been set");
|
throw new IllegalStateException("initiator RTP Content Map has not been set");
|
||||||
}
|
}
|
||||||
|
final SessionDescription offer;
|
||||||
|
try {
|
||||||
|
offer = SessionDescription.of(rtpContentMap);
|
||||||
|
} catch (final IllegalArgumentException e) {
|
||||||
|
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": unable to process offer", e);
|
||||||
|
//TODO terminate session with application error
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sendSessionAccept(offer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendSessionAccept(SessionDescription offer) {
|
||||||
discoverIceServers(iceServers -> {
|
discoverIceServers(iceServers -> {
|
||||||
setupWebRTC(iceServers);
|
setupWebRTC(iceServers);
|
||||||
final org.webrtc.SessionDescription offer = new org.webrtc.SessionDescription(
|
final org.webrtc.SessionDescription sdp = new org.webrtc.SessionDescription(
|
||||||
org.webrtc.SessionDescription.Type.OFFER,
|
org.webrtc.SessionDescription.Type.OFFER,
|
||||||
SessionDescription.of(rtpContentMap).toString()
|
offer.toString()
|
||||||
);
|
);
|
||||||
try {
|
try {
|
||||||
this.webRTCWrapper.setRemoteDescription(offer).get();
|
this.webRTCWrapper.setRemoteDescription(sdp).get();
|
||||||
org.webrtc.SessionDescription webRTCSessionDescription = this.webRTCWrapper.createAnswer().get();
|
org.webrtc.SessionDescription webRTCSessionDescription = this.webRTCWrapper.createAnswer().get();
|
||||||
final SessionDescription sessionDescription = SessionDescription.parse(webRTCSessionDescription.description);
|
final SessionDescription sessionDescription = SessionDescription.parse(webRTCSessionDescription.description);
|
||||||
final RtpContentMap respondingRtpContentMap = RtpContentMap.of(sessionDescription);
|
final RtpContentMap respondingRtpContentMap = RtpContentMap.of(sessionDescription);
|
||||||
|
|
|
@ -158,32 +158,80 @@ public class SessionDescription {
|
||||||
}
|
}
|
||||||
final ImmutableList.Builder<Integer> formatBuilder = new ImmutableList.Builder<>();
|
final ImmutableList.Builder<Integer> formatBuilder = new ImmutableList.Builder<>();
|
||||||
for (RtpDescription.PayloadType payloadType : description.getPayloadTypes()) {
|
for (RtpDescription.PayloadType payloadType : description.getPayloadTypes()) {
|
||||||
|
final String id = payloadType.getId();
|
||||||
|
if (Strings.isNullOrEmpty(id)) {
|
||||||
|
throw new IllegalArgumentException("Payload type is missing id");
|
||||||
|
}
|
||||||
|
if (!isInt(id)) {
|
||||||
|
throw new IllegalArgumentException("Payload id is not numeric");
|
||||||
|
}
|
||||||
formatBuilder.add(payloadType.getIntId());
|
formatBuilder.add(payloadType.getIntId());
|
||||||
mediaAttributes.put("rtpmap", payloadType.toSdpAttribute());
|
mediaAttributes.put("rtpmap", payloadType.toSdpAttribute());
|
||||||
List<RtpDescription.Parameter> parameters = payloadType.getParameters();
|
List<RtpDescription.Parameter> parameters = payloadType.getParameters();
|
||||||
if (parameters.size() > 0) {
|
if (parameters.size() > 0) {
|
||||||
mediaAttributes.put("fmtp", RtpDescription.Parameter.toSdpString(payloadType.getId(), parameters));
|
mediaAttributes.put("fmtp", RtpDescription.Parameter.toSdpString(id, parameters));
|
||||||
}
|
}
|
||||||
for (RtpDescription.FeedbackNegotiation feedbackNegotiation : payloadType.getFeedbackNegotiations()) {
|
for (RtpDescription.FeedbackNegotiation feedbackNegotiation : payloadType.getFeedbackNegotiations()) {
|
||||||
mediaAttributes.put("rtcp-fb", payloadType.getId() + " " + feedbackNegotiation.getType() + (Strings.isNullOrEmpty(feedbackNegotiation.getSubType()) ? "" : " " + feedbackNegotiation.getSubType()));
|
final String type = feedbackNegotiation.getType();
|
||||||
|
final String subtype = feedbackNegotiation.getSubType();
|
||||||
|
if (Strings.isNullOrEmpty(type)) {
|
||||||
|
throw new IllegalArgumentException("a feedback for payload-type " + id + " negotiation is missing type");
|
||||||
|
}
|
||||||
|
mediaAttributes.put("rtcp-fb", id + " " + type + (Strings.isNullOrEmpty(subtype) ? "" : " " + subtype));
|
||||||
}
|
}
|
||||||
for (RtpDescription.FeedbackNegotiationTrrInt feedbackNegotiationTrrInt : payloadType.feedbackNegotiationTrrInts()) {
|
for (RtpDescription.FeedbackNegotiationTrrInt feedbackNegotiationTrrInt : payloadType.feedbackNegotiationTrrInts()) {
|
||||||
mediaAttributes.put("rtcp-fb", payloadType.getId() + " trr-int " + feedbackNegotiationTrrInt.getValue());
|
mediaAttributes.put("rtcp-fb", id + " trr-int " + feedbackNegotiationTrrInt.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (RtpDescription.FeedbackNegotiation feedbackNegotiation : description.getFeedbackNegotiations()) {
|
for (RtpDescription.FeedbackNegotiation feedbackNegotiation : description.getFeedbackNegotiations()) {
|
||||||
mediaAttributes.put("rtcp-fb", "* " + feedbackNegotiation.getType() + (Strings.isNullOrEmpty(feedbackNegotiation.getSubType()) ? "" : " " + feedbackNegotiation.getSubType()));
|
final String type = feedbackNegotiation.getType();
|
||||||
|
final String subtype = feedbackNegotiation.getSubType();
|
||||||
|
if (Strings.isNullOrEmpty(type)) {
|
||||||
|
throw new IllegalArgumentException("a feedback negotiation is missing type");
|
||||||
|
}
|
||||||
|
mediaAttributes.put("rtcp-fb", "* " + type + (Strings.isNullOrEmpty(subtype) ? "" : " " + subtype));
|
||||||
}
|
}
|
||||||
for (RtpDescription.FeedbackNegotiationTrrInt feedbackNegotiationTrrInt : description.feedbackNegotiationTrrInts()) {
|
for (RtpDescription.FeedbackNegotiationTrrInt feedbackNegotiationTrrInt : description.feedbackNegotiationTrrInts()) {
|
||||||
mediaAttributes.put("rtcp-fb", "* trr-int " + feedbackNegotiationTrrInt.getValue());
|
mediaAttributes.put("rtcp-fb", "* trr-int " + feedbackNegotiationTrrInt.getValue());
|
||||||
}
|
}
|
||||||
for (RtpDescription.RtpHeaderExtension extension : description.getHeaderExtensions()) {
|
for (RtpDescription.RtpHeaderExtension extension : description.getHeaderExtensions()) {
|
||||||
mediaAttributes.put("extmap", extension.getId() + " " + extension.getUri());
|
final String id = extension.getId();
|
||||||
|
final String uri = extension.getUri();
|
||||||
|
if (Strings.isNullOrEmpty(id)) {
|
||||||
|
throw new IllegalArgumentException("A header extension is missing id");
|
||||||
|
}
|
||||||
|
if (Strings.isNullOrEmpty(uri)) {
|
||||||
|
throw new IllegalArgumentException("A header extension is missing uri");
|
||||||
|
}
|
||||||
|
mediaAttributes.put("extmap", id + " " + uri);
|
||||||
|
}
|
||||||
|
for (RtpDescription.SourceGroup sourceGroup : description.getSourceGroups()) {
|
||||||
|
final String semantics = sourceGroup.getSemantics();
|
||||||
|
final List<String> groups = sourceGroup.getSsrcs();
|
||||||
|
if (Strings.isNullOrEmpty(semantics)) {
|
||||||
|
throw new IllegalArgumentException("A SSRC group is missing semantics attribute");
|
||||||
|
}
|
||||||
|
if (groups.size() == 0) {
|
||||||
|
throw new IllegalArgumentException("A SSRC group is missing SSRC ids");
|
||||||
|
}
|
||||||
|
mediaAttributes.put("ssrc-group", String.format("%s %s", semantics, Joiner.on(' ').join(groups)));
|
||||||
}
|
}
|
||||||
for (RtpDescription.Source source : description.getSources()) {
|
for (RtpDescription.Source source : description.getSources()) {
|
||||||
for (RtpDescription.Source.Parameter parameter : source.getParameters()) {
|
for (RtpDescription.Source.Parameter parameter : source.getParameters()) {
|
||||||
mediaAttributes.put("ssrc", source.getSsrcId() + " " + parameter.getParameterName() + ":" + parameter.getParameterValue());
|
final String id = source.getSsrcId();
|
||||||
|
final String parameterName = parameter.getParameterName();
|
||||||
|
final String parameterValue = parameter.getParameterValue();
|
||||||
|
if (Strings.isNullOrEmpty(id)) {
|
||||||
|
throw new IllegalArgumentException("A source specific media attribute is missing the id");
|
||||||
|
}
|
||||||
|
if (Strings.isNullOrEmpty(parameterName)) {
|
||||||
|
throw new IllegalArgumentException("A source specific media attribute is missing its name");
|
||||||
|
}
|
||||||
|
if (Strings.isNullOrEmpty(parameterValue)) {
|
||||||
|
throw new IllegalArgumentException("A source specific media attribute is missing its value");
|
||||||
|
}
|
||||||
|
mediaAttributes.put("ssrc", id + " " + parameter.getParameterName() + ":" + parameter.getParameterValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,6 +268,18 @@ public class SessionDescription {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isInt(final String input) {
|
||||||
|
if (input == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Integer.parseInt(input);
|
||||||
|
return true;
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static Pair<String, String> parseAttribute(final String input) {
|
public static Pair<String, String> parseAttribute(final String input) {
|
||||||
final String[] pair = input.split(":", 2);
|
final String[] pair = input.split(":", 2);
|
||||||
if (pair.length == 2) {
|
if (pair.length == 2) {
|
||||||
|
@ -233,6 +293,7 @@ public class SessionDescription {
|
||||||
public String toString() {
|
public String toString() {
|
||||||
final StringBuilder s = new StringBuilder()
|
final StringBuilder s = new StringBuilder()
|
||||||
.append("v=").append(version).append(LINE_DIVIDER)
|
.append("v=").append(version).append(LINE_DIVIDER)
|
||||||
|
//TODO randomize or static
|
||||||
.append("o=- 8770656990916039506 2 IN IP4 127.0.0.1").append(LINE_DIVIDER) //what ever that means
|
.append("o=- 8770656990916039506 2 IN IP4 127.0.0.1").append(LINE_DIVIDER) //what ever that means
|
||||||
.append("s=").append(name).append(LINE_DIVIDER)
|
.append("s=").append(name).append(LINE_DIVIDER)
|
||||||
.append("t=0 0").append(LINE_DIVIDER);
|
.append("t=0 0").append(LINE_DIVIDER);
|
||||||
|
|
|
@ -70,6 +70,16 @@ public class RtpDescription extends GenericDescription {
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<SourceGroup> getSourceGroups() {
|
||||||
|
final ImmutableList.Builder<SourceGroup> builder = new ImmutableList.Builder<>();
|
||||||
|
for (final Element child : this.children) {
|
||||||
|
if ("ssrc-group".equals(child.getName()) && Namespace.JINGLE_RTP_SOURCE_SPECIFIC_MEDIA_ATTRIBUTES.equals(child.getNamespace())) {
|
||||||
|
builder.add(SourceGroup.upgrade(child));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
public static RtpDescription upgrade(final Element element) {
|
public static RtpDescription upgrade(final Element element) {
|
||||||
Preconditions.checkArgument("description".equals(element.getName()), "Name of provided element is not description");
|
Preconditions.checkArgument("description".equals(element.getName()), "Name of provided element is not description");
|
||||||
Preconditions.checkArgument(Namespace.JINGLE_APPS_RTP.equals(element.getNamespace()), "Element does not match the jingle rtp namespace");
|
Preconditions.checkArgument(Namespace.JINGLE_APPS_RTP.equals(element.getNamespace()), "Element does not match the jingle rtp namespace");
|
||||||
|
@ -458,6 +468,39 @@ public class RtpDescription extends GenericDescription {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class SourceGroup extends Element {
|
||||||
|
|
||||||
|
private SourceGroup() {
|
||||||
|
super("ssrc-group", Namespace.JINGLE_RTP_SOURCE_SPECIFIC_MEDIA_ATTRIBUTES);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSemantics() {
|
||||||
|
return this.getAttribute("semantics");
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getSsrcs() {
|
||||||
|
ImmutableList.Builder<String> builder = new ImmutableList.Builder<>();
|
||||||
|
for(Element child : this.children) {
|
||||||
|
if ("source".equals(child.getName())) {
|
||||||
|
final String ssrc = child.getAttribute("ssrc");
|
||||||
|
if (ssrc != null) {
|
||||||
|
builder.add(ssrc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SourceGroup upgrade(final Element element) {
|
||||||
|
Preconditions.checkArgument("ssrc-group".equals(element.getName()));
|
||||||
|
Preconditions.checkArgument(Namespace.JINGLE_RTP_SOURCE_SPECIFIC_MEDIA_ATTRIBUTES.equals(element.getNamespace()));
|
||||||
|
final SourceGroup group = new SourceGroup();
|
||||||
|
group.setChildren(element.getChildren());
|
||||||
|
group.setAttributes(element.getAttributes());
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public enum Media {
|
public enum Media {
|
||||||
VIDEO, AUDIO, UNKNOWN;
|
VIDEO, AUDIO, UNKNOWN;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue