VoIPCallTests.swift 52 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937
  1. // _____ _
  2. // |_ _| |_ _ _ ___ ___ _ __ __ _
  3. // | | | ' \| '_/ -_) -_) ' \/ _` |_
  4. // |_| |_||_|_| \___\___|_|_|_\__,_(_)
  5. //
  6. // Threema iOS Client
  7. // Copyright (c) 2019-2020 Threema GmbH
  8. //
  9. // This program is free software: you can redistribute it and/or modify
  10. // it under the terms of the GNU Affero General Public License, version 3,
  11. // as published by the Free Software Foundation.
  12. //
  13. // This program is distributed in the hope that it will be useful,
  14. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. // GNU Affero General Public License for more details.
  17. //
  18. // You should have received a copy of the GNU Affero General Public License
  19. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  20. import XCTest
  21. @testable import Threema
  22. class SdpPatcherTests: XCTestCase {
  23. override func setUp() {
  24. super.setUp()
  25. // necessary for ValidationLogger
  26. AppGroup.setGroupId("group.ch.threema") //THREEMA_GROUP_IDENTIFIER @"group.ch.threema"
  27. }
  28. override func tearDown() {
  29. super.tearDown()
  30. }
  31. /// If the "cbr" parameter is already contained in the fmtp attribute, it should be updated.
  32. func testPatchForceCbr() {
  33. let actual = "v=0\r\n" +
  34. "m=audio 9 UDP/TLS/RTP/SAVPF 111\r\n" +
  35. "a=rtpmap:111 opus/48000/2\r\n" +
  36. "a=fmtp:111 minptime=10;cbr=0;useinbandfec=1\r\n"
  37. let expected = "v=0\r\n" +
  38. "m=audio 9 UDP/TLS/RTP/SAVPF 111\r\n" +
  39. "a=rtpmap:111 opus/48000/2\r\n" +
  40. "a=fmtp:111 minptime=10;useinbandfec=1;stereo=0;sprop-stereo=0;cbr=1\r\n"
  41. XCTAssertEqual(expected, try VoIPCallSdpPatcher().patch(type: .LOCAL_ANSWER_OR_REMOTE_SDP, sdp: actual))
  42. XCTAssertEqual(expected, try VoIPCallSdpPatcher().patch(type: .LOCAL_OFFER, sdp: actual))
  43. }
  44. /// If the fmtp line contains parameters without an '=' sign, nothing should break.
  45. func testPatchInvalidFmtpLine() {
  46. let actual = "v=0\r\n" +
  47. "m=audio 9 UDP/TLS/RTP/SAVPF 111\r\n" +
  48. "a=rtpmap:111 opus/48000/2\r\n" +
  49. "a=fmtp:111 minptime=10;cbr0;useinbandfec=1\r\n" +
  50. "a=fmtp:1337 cat=yes;duck=no\r\n"
  51. let expected = "v=0\r\n" +
  52. "m=audio 9 UDP/TLS/RTP/SAVPF 111\r\n" +
  53. "a=rtpmap:111 opus/48000/2\r\n" +
  54. "a=fmtp:111 minptime=10;cbr0;useinbandfec=1;stereo=0;sprop-stereo=0;cbr=1\r\n"
  55. XCTAssertEqual(expected, try VoIPCallSdpPatcher().patch(type: .LOCAL_ANSWER_OR_REMOTE_SDP, sdp: actual))
  56. XCTAssertEqual(expected, try VoIPCallSdpPatcher().patch(type: .LOCAL_OFFER, sdp: actual))
  57. }
  58. /// Non-Opus fmtp lines should be dropped.
  59. func testPatchIgnoreNonOpusFmtpLine() {
  60. let actual = "v=0\r\n" +
  61. "m=audio 9 UDP/TLS/RTP/SAVPF 111\r\n" +
  62. "a=rtpmap:111 opus/48000/2\r\n" +
  63. "a=fmtp:1337 cat=yes;duck=no\r\n" +
  64. "a=fmtp:111 minptime=10;useinbandfec=1\r\n"
  65. let expected = "v=0\r\n" +
  66. "m=audio 9 UDP/TLS/RTP/SAVPF 111\r\n" +
  67. "a=rtpmap:111 opus/48000/2\r\n" +
  68. "a=fmtp:111 minptime=10;useinbandfec=1;stereo=0;sprop-stereo=0;cbr=1\r\n"
  69. XCTAssertEqual(expected, try VoIPCallSdpPatcher().patch(type: .LOCAL_ANSWER_OR_REMOTE_SDP, sdp: actual))
  70. XCTAssertEqual(expected, try VoIPCallSdpPatcher().patch(type: .LOCAL_OFFER, sdp: actual))
  71. }
  72. /// We always require Opus, so it should throw an exception if it's not present.
  73. func testPatchWithoutOpusAnswer() {
  74. let sdp = "v=0\r\n" +
  75. "m=audio 9 UDP/TLS/RTP/SAVPF 111\r\n" +
  76. "a=rtpmap:111 popus/48000/2\r\n" +
  77. "a=urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n" +
  78. "a=fmtp:111 minptime=10;cbr0;useinbandfec=1\r\n"
  79. XCTAssertThrowsError(try VoIPCallSdpPatcher().patch(type: .LOCAL_ANSWER_OR_REMOTE_SDP, sdp: sdp), "") { (error) in
  80. let sdpError = error as! VoIPCallSdpPatcher.SdpError
  81. XCTAssertEqual(sdpError.type, VoIPCallSdpPatcher.SdpErrorType.invalidSdp)
  82. XCTAssertEqual(sdpError.description, "a=rtpmap: [...] opus not found")
  83. }
  84. }
  85. /// We always require Opus, so it should throw an exception if it's not present.
  86. func testPatchWithoutOpusOffer() {
  87. let sdp = "v=0\r\n" +
  88. "m=audio 9 UDP/TLS/RTP/SAVPF 111\r\n" +
  89. "a=rtpmap:111 popus/48000/2\r\n" +
  90. "a=urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n" +
  91. "a=fmtp:111 minptime=10;cbr0;useinbandfec=1\r\n"
  92. XCTAssertThrowsError(try VoIPCallSdpPatcher().patch(type: .LOCAL_OFFER, sdp: sdp), "") { (error) in
  93. let sdpError = error as! VoIPCallSdpPatcher.SdpError
  94. XCTAssertEqual(sdpError.type, VoIPCallSdpPatcher.SdpErrorType.invalidSdp)
  95. XCTAssertEqual(sdpError.description, "a=rtpmap: [...] opus not found")
  96. }
  97. }
  98. /// It should throw an exception if the Opus payload type cannot be found in the RTP map.
  99. func testPatchWithoutOpusPayloadTypeAnswer() {
  100. let sdp = "v=0\r\n" +
  101. "m=audio 9 UDP/TLS/RTP/SAVPF 1337\r\n" +
  102. "a=rtpmap:111 opus/48000/2\r\n"
  103. XCTAssertThrowsError(try VoIPCallSdpPatcher().patch(type: .LOCAL_ANSWER_OR_REMOTE_SDP, sdp: sdp), "") { (error) in
  104. let sdpError = error as! VoIPCallSdpPatcher.SdpError
  105. XCTAssertEqual(sdpError.type, VoIPCallSdpPatcher.SdpErrorType.invalidSdp)
  106. XCTAssertEqual(sdpError.description, "a=rtpmap: [...] opus not found")
  107. }
  108. }
  109. /// It should throw an exception if the Opus payload type cannot be found in the RTP map.
  110. func testPatchWithoutOpusPayloadTypeOffer() {
  111. let sdp = "v=0\r\n" +
  112. "m=audio 9 UDP/TLS/RTP/SAVPF 1337\r\n" +
  113. "a=rtpmap:111 opus/48000/2\r\n"
  114. XCTAssertThrowsError(try VoIPCallSdpPatcher().patch(type: .LOCAL_OFFER, sdp: sdp), "") { (error) in
  115. let sdpError = error as! VoIPCallSdpPatcher.SdpError
  116. XCTAssertEqual(sdpError.type, VoIPCallSdpPatcher.SdpErrorType.invalidSdp)
  117. XCTAssertEqual(sdpError.description, "a=rtpmap: [...] opus not found")
  118. }
  119. }
  120. /// Unknown media sections should be completely stripped.
  121. func testPatchWithUnknownMedia() {
  122. let actual = "v=0\r\n" +
  123. "m=audio 9 UDP/TLS/RTP/SAVPF 111\r\n" +
  124. "a=rtpmap:111 opus/48000/2\r\n" +
  125. "m=everything in plain text over the wire kthx\r\n" +
  126. "a=plaintext OH YES YES YES\r\n" +
  127. "a=moar-plaintext\r\n" +
  128. "m=application 9 UDP/DTLS/SCTP webrtc-datachannel\r\n" +
  129. "a=sctp-port:5000\r\n" +
  130. "m=the-train-protocol\r\n" +
  131. "a=choo-chooo\r\n"
  132. let expected = "v=0\r\n" +
  133. "m=audio 9 UDP/TLS/RTP/SAVPF 111\r\n" +
  134. "a=rtpmap:111 opus/48000/2\r\n" +
  135. "m=application 9 UDP/DTLS/SCTP webrtc-datachannel\r\n" +
  136. "a=sctp-port:5000\r\n"
  137. XCTAssertEqual(expected, try VoIPCallSdpPatcher().patch(type: .LOCAL_ANSWER_OR_REMOTE_SDP, sdp: actual))
  138. XCTAssertEqual(expected, try VoIPCallSdpPatcher().patch(type: .LOCAL_OFFER, sdp: actual))
  139. }
  140. /// Ensure data channel sections aren't stripped.
  141. func testPatchWithDataChannelMedia() {
  142. let sdp = "v=0\r\n" +
  143. "m=audio 9 UDP/TLS/RTP/SAVPF 111\r\n" +
  144. "a=rtpmap:111 opus/48000/2\r\n" +
  145. "m=application 9 UDP/DTLS/SCTP webrtc-datachannel\r\n" +
  146. "a=sctp-port:5000\r\n"
  147. XCTAssertEqual(sdp, try VoIPCallSdpPatcher().patch(type: .LOCAL_ANSWER_OR_REMOTE_SDP, sdp: sdp))
  148. XCTAssertEqual(sdp, try VoIPCallSdpPatcher().patch(type: .LOCAL_OFFER, sdp: sdp))
  149. }
  150. /// When RTP header extensions have been disabled, ensure the lines are stripped.
  151. func testPatchWithNoRtpHeaderExtensions() {
  152. let actual = "v=0\r\n" +
  153. "m=audio 9 UDP/TLS/RTP/SAVPF 111\r\n" +
  154. "a=rtpmap:111 opus/48000/2\r\n" +
  155. "a=extmap:6 urn:ietf:params:rtp-hdrext:encrypt 6-1\r\n" +
  156. "a=extmap:6 urn:ietf:params:rtp-hdrext:encrypt 6-2\r\n" +
  157. "a=extmap:5 urn:ietf:params:rtp-hdrext:encrypt 5\r\n" +
  158. "a=extmap:3 urn:ietf:params:rtp-hdrext:encrypt 3\r\n" +
  159. "a=extmap:1 urn:ietf:params:rtp-hdrext:encrypt 1\r\n" +
  160. "a=extmap:7 urn:ietf:params:rtp-hdrext:encrypt 7\r\n" +
  161. "a=extmap:8 urn:ietf:params:rtp-hdrext:encrypt 8\r\n" +
  162. "a=extmap:9 urn:ietf:params:rtp-hdrext:encrypt 9\r\n" +
  163. "a=extmap:11 urn:ietf:params:rtp-hdrext:encrypt 11\r\n" +
  164. "a=extmap:10 urn:ietf:params:rtp-hdrext:encrypt 10\r\n" +
  165. "a=extmap:12 urn:ietf:params:rtp-hdrext:encrypt 12\r\n" +
  166. "a=extmap:15 urn:ietf:params:rtp-hdrext:encrypt 15\r\n" +
  167. "a=extmap:19 urn:ietf:params:rtp-hdrext:encrypt 19\r\n" +
  168. "a=extmap:1337387126438213678123681273618 urn:ietf:params:rtp-hdrext:encrypt 1337387126438213678123681273618\r\n"
  169. let expected = "v=0\r\n" +
  170. "m=audio 9 UDP/TLS/RTP/SAVPF 111\r\n" +
  171. "a=rtpmap:111 opus/48000/2\r\n"
  172. XCTAssertEqual(expected, try VoIPCallSdpPatcher(.DISABLE).patch(type: .LOCAL_OFFER, sdp: actual))
  173. XCTAssertEqual(expected, try VoIPCallSdpPatcher(.DISABLE).patch(type: .LOCAL_ANSWER_OR_REMOTE_SDP, sdp: actual))
  174. XCTAssertEqual(expected, try VoIPCallSdpPatcher().patch(type: .LOCAL_OFFER, sdp: actual))
  175. XCTAssertEqual(expected, try VoIPCallSdpPatcher().patch(type: .LOCAL_ANSWER_OR_REMOTE_SDP, sdp: actual))
  176. }
  177. /// When local offer, in one-byte header mode, RTP header extension IDs should be reassigned in the range from 1-14.
  178. func testPatchWithRtpOneByteModeHeaderIdsReassigned() {
  179. let actual = "v=0\r\n" +
  180. "m=audio 9 UDP/TLS/RTP/SAVPF 111\r\n" +
  181. "a=rtpmap:111 opus/48000/2\r\n" +
  182. "a=extmap:6 urn:ietf:params:rtp-hdrext:encrypt 6-1\r\n" +
  183. "a=extmap:6 urn:ietf:params:rtp-hdrext:encrypt 6-2\r\n" +
  184. "a=extmap:5 urn:ietf:params:rtp-hdrext:encrypt 5\r\n" +
  185. "a=extmap:3 urn:ietf:params:rtp-hdrext:encrypt 3\r\n" +
  186. "a=extmap:1 urn:ietf:params:rtp-hdrext:encrypt 1\r\n" +
  187. "a=extmap:7 urn:ietf:params:rtp-hdrext:encrypt 7\r\n" +
  188. "a=extmap:8 urn:ietf:params:rtp-hdrext:encrypt 8\r\n" +
  189. "a=extmap:9 urn:ietf:params:rtp-hdrext:encrypt 9\r\n" +
  190. "a=extmap:11 urn:ietf:params:rtp-hdrext:encrypt 11\r\n" +
  191. "a=extmap:10 urn:ietf:params:rtp-hdrext:encrypt 10\r\n" +
  192. "a=extmap:12 urn:ietf:params:rtp-hdrext:encrypt 12\r\n" +
  193. "a=extmap:15 urn:ietf:params:rtp-hdrext:encrypt 15\r\n" +
  194. "a=extmap:19 urn:ietf:params:rtp-hdrext:encrypt 19\r\n" +
  195. "a=extmap:1337387126438213678123681273618 urn:ietf:params:rtp-hdrext:encrypt 1337387126438213678123681273618\r\n"
  196. let expected = "v=0\r\n" +
  197. "m=audio 9 UDP/TLS/RTP/SAVPF 111\r\n" +
  198. "a=rtpmap:111 opus/48000/2\r\n" +
  199. "a=extmap:1 urn:ietf:params:rtp-hdrext:encrypt 6-1\r\n" +
  200. "a=extmap:2 urn:ietf:params:rtp-hdrext:encrypt 6-2\r\n" +
  201. "a=extmap:3 urn:ietf:params:rtp-hdrext:encrypt 5\r\n" +
  202. "a=extmap:4 urn:ietf:params:rtp-hdrext:encrypt 3\r\n" +
  203. "a=extmap:5 urn:ietf:params:rtp-hdrext:encrypt 1\r\n" +
  204. "a=extmap:6 urn:ietf:params:rtp-hdrext:encrypt 7\r\n" +
  205. "a=extmap:7 urn:ietf:params:rtp-hdrext:encrypt 8\r\n" +
  206. "a=extmap:8 urn:ietf:params:rtp-hdrext:encrypt 9\r\n" +
  207. "a=extmap:9 urn:ietf:params:rtp-hdrext:encrypt 11\r\n" +
  208. "a=extmap:10 urn:ietf:params:rtp-hdrext:encrypt 10\r\n" +
  209. "a=extmap:11 urn:ietf:params:rtp-hdrext:encrypt 12\r\n" +
  210. "a=extmap:12 urn:ietf:params:rtp-hdrext:encrypt 15\r\n" +
  211. "a=extmap:13 urn:ietf:params:rtp-hdrext:encrypt 19\r\n" +
  212. "a=extmap:14 urn:ietf:params:rtp-hdrext:encrypt 1337387126438213678123681273618\r\n"
  213. XCTAssertEqual(expected, try VoIPCallSdpPatcher(.ENABLE_WITH_LEGACY_ONE_BYTE_HEADER_ONLY).patch(type: .LOCAL_OFFER, sdp: actual))
  214. }
  215. /// When local offer, in one-byte header mode, more than 14 RTP header extensions are not allowed.
  216. func testPatchWithRtpOneByteModeHeaderMoreThan14Offer() {
  217. let sdp = "v=0\r\n" +
  218. "m=audio 9 UDP/TLS/RTP/SAVPF 111\r\n" +
  219. "a=rtpmap:111 opus/48000/2\r\n" +
  220. "a=extmap:6 urn:ietf:params:rtp-hdrext:encrypt 6-1\r\n" +
  221. "a=extmap:6 urn:ietf:params:rtp-hdrext:encrypt 6-2\r\n" +
  222. "a=extmap:5 urn:ietf:params:rtp-hdrext:encrypt 5\r\n" +
  223. "a=extmap:3 urn:ietf:params:rtp-hdrext:encrypt 3\r\n" +
  224. "a=extmap:1 urn:ietf:params:rtp-hdrext:encrypt 1\r\n" +
  225. "a=extmap:7 urn:ietf:params:rtp-hdrext:encrypt 7\r\n" +
  226. "a=extmap:8 urn:ietf:params:rtp-hdrext:encrypt 8\r\n" +
  227. "a=extmap:9 urn:ietf:params:rtp-hdrext:encrypt 9\r\n" +
  228. "a=extmap:11 urn:ietf:params:rtp-hdrext:encrypt 11\r\n" +
  229. "a=extmap:10 urn:ietf:params:rtp-hdrext:encrypt 10\r\n" +
  230. "a=extmap:12 urn:ietf:params:rtp-hdrext:encrypt 12\r\n" +
  231. "a=extmap:15 urn:ietf:params:rtp-hdrext:encrypt 15\r\n" +
  232. "a=extmap:19 urn:ietf:params:rtp-hdrext:encrypt 19\r\n" +
  233. "a=extmap:20 urn:ietf:params:rtp-hdrext:encrypt 20\r\n" +
  234. "a=extmap:1337387126438213678123681273618 urn:ietf:params:rtp-hdrext:encrypt 1337387126438213678123681273618\r\n"
  235. XCTAssertThrowsError(try VoIPCallSdpPatcher(.ENABLE_WITH_LEGACY_ONE_BYTE_HEADER_ONLY).patch(type: .LOCAL_OFFER, sdp: sdp), "") { (error) in
  236. let sdpError = error as! VoIPCallSdpPatcher.SdpError
  237. XCTAssertEqual(sdpError.type, VoIPCallSdpPatcher.SdpErrorType.invalidSdp)
  238. XCTAssertEqual(sdpError.description, "a=rtpmap: [...] opus not found")
  239. }
  240. }
  241. /// When local answer or remote SDP, in one-byte header mode, RTP header extension IDs should not be reassigned and more than 14 header extensions will be accepted, too.
  242. func testPatchWithRtpOneByteModeHeaderMoreThan14AnswerOrRemote() {
  243. let sdp = "v=0\r\n" +
  244. "m=audio 9 UDP/TLS/RTP/SAVPF 111\r\n" +
  245. "a=rtpmap:111 opus/48000/2\r\n" +
  246. "a=extmap:6 urn:ietf:params:rtp-hdrext:encrypt 6-1\r\n" +
  247. "a=extmap:6 urn:ietf:params:rtp-hdrext:encrypt 6-2\r\n" +
  248. "a=extmap:5 urn:ietf:params:rtp-hdrext:encrypt 5\r\n" +
  249. "a=extmap:3 urn:ietf:params:rtp-hdrext:encrypt 3\r\n" +
  250. "a=extmap:1 urn:ietf:params:rtp-hdrext:encrypt 1\r\n" +
  251. "a=extmap:7 urn:ietf:params:rtp-hdrext:encrypt 7\r\n" +
  252. "a=extmap:8 urn:ietf:params:rtp-hdrext:encrypt 8\r\n" +
  253. "a=extmap:9 urn:ietf:params:rtp-hdrext:encrypt 9\r\n" +
  254. "a=extmap:11 urn:ietf:params:rtp-hdrext:encrypt 11\r\n" +
  255. "a=extmap:10 urn:ietf:params:rtp-hdrext:encrypt 10\r\n" +
  256. "a=extmap:12 urn:ietf:params:rtp-hdrext:encrypt 12\r\n" +
  257. "a=extmap:15 urn:ietf:params:rtp-hdrext:encrypt 15\r\n" +
  258. "a=extmap:19 urn:ietf:params:rtp-hdrext:encrypt 19\r\n" +
  259. "a=extmap:1337387126438213678123681273618 urn:ietf:params:rtp-hdrext:encrypt 1337387126438213678123681273618\r\n" +
  260. "a=extmap:1337387126438213678123681273618 urn:ietf:params:rtp-hdrext:encrypt 1337387126438213678123681273618\r\n"
  261. XCTAssertEqual(sdp, try VoIPCallSdpPatcher(.ENABLE_WITH_LEGACY_ONE_BYTE_HEADER_ONLY).patch(type: .LOCAL_ANSWER_OR_REMOTE_SDP, sdp: sdp))
  262. }
  263. /// When local offer, in two-byte (mixed) header mode, RTP header extension IDs should be reassigned in the range from 1-14 and 16-255.
  264. func testPatchWithRtpMixedModeHeaderIdsReassigned() {
  265. var actual = "v=0\r\n" +
  266. "m=audio 9 UDP/TLS/RTP/SAVPF 111\r\n" +
  267. "a=rtpmap:111 opus/48000/2\r\n"
  268. var expected = "v=0\r\n" +
  269. "m=audio 9 UDP/TLS/RTP/SAVPF 111\r\n" +
  270. "a=rtpmap:111 opus/48000/2\r\n"
  271. let ids: [Int] = [134, 68, 14, 95, 97, 64, 66, 89, 143, 82, 51, 53, 145, 172, 91, 64, 144, 107, 241, 37,
  272. 244, 233, 108, 158, 17, 185, 73, 88, 181, 226, 180, 95, 91, 106, 68, 220, 42, 116, 134,
  273. 102, 63, 193, 135, 248, 141, 1, 157, 116, 34, 251, 218, 33, 90, 124, 28, 163, 22, 129,
  274. 73, 234, 138, 93, 224, 220, 150, 6, 56, 55, 112, 112, 210, 189, 150, 202, 197, 74, 176,
  275. 170, 218, 234, 203, 244, 205, 170, 160, 165, 67, 156, 222, 107, 5, 220, 167, 49, 160,
  276. 3, 243, 194, 60, 230, 164, 241, 88, 183, 143, 192, 111, 139, 233, 235, 71, 30, 30, 117,
  277. 188, 245, 230, 92, 37, 82, 119, 236, 250, 2, 123, 188, 243, 196, 220, 177, 172, 105,
  278. 120, 217, 32, 67, 169, 170, 37, 16, 132, 128, 67, 87, 191, 75, 21, 240, 96, 2, 72, 31,
  279. 23, 126, 137, 134, 209, 249, 67, 139, 19, 31, 173, 135, 41, 151, 42, 161, 44, 111, 165,
  280. 193, 243, 216, 248, 89, 251, 140, 97, 18, 22, 47, 108, 50, 35, 188, 186, 212, 43, 80,
  281. 21, 16, 144, 173, 92, 53, 3, 162, 126, 208, 117, 160, 238, 140, 4, 87, 227, 231, 49,
  282. 82, 235, 156, 1, 99, 30, 154, 178, 45, 92, 246, 148, 100, 218, 27, 146, 193, 81, 179,
  283. 197, 68, 221, 59, 237, 74, 249, 1, 73, 234, 15, 63, 158, 23, 166, 36, 1, 76, 101, 180,
  284. 176, 162, 52, 254, 1337, 564654655]
  285. XCTAssertEqual(ids.count, 254)
  286. for (index, currentId) in ids.enumerated() {
  287. actual.append(String(format:"a=extmap:%i urn:ietf:params:rtp-hdrext:encrypt %i\r\n", currentId, index))
  288. var id = index + 1
  289. if id >= 15 {
  290. id += 1
  291. }
  292. expected.append(String(format:"a=extmap:%i urn:ietf:params:rtp-hdrext:encrypt %i\r\n", id, index))
  293. }
  294. XCTAssertEqual(expected, try VoIPCallSdpPatcher(.ENABLE_WITH_ONE_AND_TWO_BYTE_HEADER).patch(type: .LOCAL_OFFER, sdp: actual))
  295. }
  296. /// When local offer, in two-byte header mode, more than 254 RTP header extensions are not allowed.
  297. func testPatchWithRtpMixedModeHeaderMoreThan254Offer() {
  298. var sdp = "v=0\r\n" +
  299. "m=audio 9 UDP/TLS/RTP/SAVPF 111\r\n" +
  300. "a=rtpmap:111 opus/48000/2\r\n"
  301. let ids: [Int] = [134, 68, 14, 95, 97, 64, 66, 89, 143, 82, 51, 53, 145, 172, 91, 64, 144, 107, 241, 37,
  302. 244, 233, 108, 158, 17, 185, 73, 88, 181, 226, 180, 95, 91, 106, 68, 220, 42, 116, 134,
  303. 102, 63, 193, 135, 248, 141, 1, 157, 116, 34, 251, 218, 33, 90, 124, 28, 163, 22, 129,
  304. 73, 234, 138, 93, 224, 220, 150, 6, 56, 55, 112, 112, 210, 189, 150, 202, 197, 74, 176,
  305. 170, 218, 234, 203, 244, 205, 170, 160, 165, 67, 156, 222, 107, 5, 220, 167, 49, 160,
  306. 3, 243, 194, 60, 230, 164, 241, 88, 183, 143, 192, 111, 139, 233, 235, 71, 30, 30, 117,
  307. 188, 245, 230, 92, 37, 82, 119, 236, 250, 2, 123, 188, 243, 196, 220, 177, 172, 105,
  308. 120, 217, 32, 67, 169, 170, 37, 16, 132, 128, 67, 87, 191, 75, 21, 240, 96, 2, 72, 31,
  309. 23, 126, 137, 134, 209, 249, 67, 139, 19, 31, 173, 135, 41, 151, 42, 161, 44, 111, 165,
  310. 193, 243, 216, 248, 89, 251, 140, 97, 18, 22, 47, 108, 50, 35, 188, 186, 212, 43, 80,
  311. 21, 16, 144, 173, 92, 53, 3, 162, 126, 208, 117, 160, 238, 140, 4, 87, 227, 231, 49,
  312. 82, 235, 156, 1, 99, 30, 154, 178, 45, 92, 246, 148, 100, 218, 27, 146, 193, 81, 179,
  313. 197, 68, 221, 59, 237, 74, 249, 1, 73, 234, 15, 63, 158, 23, 166, 36, 1, 76, 101, 180,
  314. 176, 162, 52, 254, 239, 236, 11, 1337, 564654655]
  315. XCTAssertEqual(ids.count, 257)
  316. for (index, currentId) in ids.enumerated() {
  317. sdp.append(String(format:"a=extmap:%i urn:ietf:params:rtp-hdrext:encrypt %i\r\n", currentId, index))
  318. }
  319. XCTAssertThrowsError(try VoIPCallSdpPatcher(.ENABLE_WITH_ONE_AND_TWO_BYTE_HEADER).patch(type: .LOCAL_OFFER, sdp: sdp), "") { (error) in
  320. let sdpError = error as! VoIPCallSdpPatcher.SdpError
  321. XCTAssertEqual(sdpError.type, VoIPCallSdpPatcher.SdpErrorType.invalidSdp)
  322. XCTAssertEqual(sdpError.description, "a=rtpmap: [...] opus not found")
  323. }
  324. }
  325. /// Ensure the `a=extmap-allow-mixed` attribute is stripped when talking to legacy apps.
  326. func testPatchMixedRtpHeaderStrippedTowardsLegacy() {
  327. let actual = "v=0\r\n" +
  328. "a=extmap-allow-mixed\r\n" +
  329. "m=audio 9 UDP/TLS/RTP/SAVPF 111\r\n" +
  330. "a=rtpmap:111 opus/48000/2\r\n" +
  331. "a=extmap-allow-mixed\r\n" +
  332. "m=video whatever\r\n" +
  333. "a=extmap-allow-mixed\r\n" +
  334. "a=extmap-allow-mixed\r\n" +
  335. "m=application 9 UDP/DTLS/SCTP webrtc-datachannel\r\n" +
  336. "a=extmap-allow-mixed\r\n" +
  337. "a=sctp-port:5000\r\n"
  338. let expected = "v=0\r\n" +
  339. "m=audio 9 UDP/TLS/RTP/SAVPF 111\r\n" +
  340. "a=rtpmap:111 opus/48000/2\r\n" +
  341. "m=video whatever\r\n" +
  342. "m=application 9 UDP/DTLS/SCTP webrtc-datachannel\r\n" +
  343. "a=extmap-allow-mixed\r\n" +
  344. "a=sctp-port:5000\r\n"
  345. XCTAssertEqual(expected, try VoIPCallSdpPatcher(.ENABLE_WITH_LEGACY_ONE_BYTE_HEADER_ONLY).patch(type: .LOCAL_ANSWER_OR_REMOTE_SDP, sdp: actual))
  346. XCTAssertEqual(expected, try VoIPCallSdpPatcher(.ENABLE_WITH_LEGACY_ONE_BYTE_HEADER_ONLY).patch(type: .LOCAL_OFFER, sdp: actual))
  347. XCTAssertEqual(expected, try VoIPCallSdpPatcher().patch(type: .LOCAL_ANSWER_OR_REMOTE_SDP, sdp: actual))
  348. XCTAssertEqual(expected, try VoIPCallSdpPatcher().patch(type: .LOCAL_OFFER, sdp: actual))
  349. }
  350. /// Ensure the `a=extmap-allow-mixed` attribute is not stripped when talking to non-legacy apps.
  351. func testPatchMixedRtpHeaderNotStrippedTowardsCurrent() {
  352. let actual = "v=0\r\n" +
  353. "a=extmap-allow-mixed\r\n" +
  354. "m=audio 9 UDP/TLS/RTP/SAVPF 111\r\n" +
  355. "a=rtpmap:111 opus/48000/2\r\n" +
  356. "a=extmap-allow-mixed\r\n" +
  357. "m=video whatever\r\n" +
  358. "a=extmap-allow-mixed\r\n" +
  359. "a=extmap-allow-mixed\r\n" +
  360. "m=application 9 UDP/DTLS/SCTP webrtc-datachannel\r\n" +
  361. "a=extmap-allow-mixed\r\n" +
  362. "a=sctp-port:5000\r\n"
  363. let expected = "v=0\r\n" +
  364. "a=extmap-allow-mixed\r\n" +
  365. "m=audio 9 UDP/TLS/RTP/SAVPF 111\r\n" +
  366. "a=rtpmap:111 opus/48000/2\r\n" +
  367. "a=extmap-allow-mixed\r\n" +
  368. "m=video whatever\r\n" +
  369. "a=extmap-allow-mixed\r\n" +
  370. "a=extmap-allow-mixed\r\n" +
  371. "m=application 9 UDP/DTLS/SCTP webrtc-datachannel\r\n" +
  372. "a=extmap-allow-mixed\r\n" +
  373. "a=sctp-port:5000\r\n"
  374. XCTAssertEqual(expected, try VoIPCallSdpPatcher(.ENABLE_WITH_ONE_AND_TWO_BYTE_HEADER).patch(type: .LOCAL_ANSWER_OR_REMOTE_SDP, sdp: actual))
  375. XCTAssertEqual(expected, try VoIPCallSdpPatcher(.ENABLE_WITH_ONE_AND_TWO_BYTE_HEADER).patch(type: .LOCAL_OFFER, sdp: actual))
  376. }
  377. /// All non-encrypted RTP header extensions should be stripped.
  378. func testPatchWitRtpHeaderUnencrypted() {
  379. let actual = "v=0\r\n" +
  380. "m=audio 9 UDP/TLS/RTP/SAVPF 111\r\n" +
  381. "a=rtpmap:111 opus/48000/2\r\n" +
  382. "a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid\r\n" +
  383. "a=extmap:5 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id\r\n" +
  384. "a=extmap:6 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id\r\n" +
  385. "a=extmap:7 duck-noises\r\n" +
  386. "a=extmap:8 urn:ietf:params:rtp-hdrext:encrypt encrypted-duck-noises\r\n"
  387. var expected = "v=0\r\n" +
  388. "m=audio 9 UDP/TLS/RTP/SAVPF 111\r\n" +
  389. "a=rtpmap:111 opus/48000/2\r\n" +
  390. "a=extmap:8 urn:ietf:params:rtp-hdrext:encrypt encrypted-duck-noises\r\n"
  391. XCTAssertEqual(expected, try VoIPCallSdpPatcher(.ENABLE_WITH_ONE_AND_TWO_BYTE_HEADER).patch(type: .LOCAL_ANSWER_OR_REMOTE_SDP, sdp: actual))
  392. expected = "v=0\r\n" +
  393. "m=audio 9 UDP/TLS/RTP/SAVPF 111\r\n" +
  394. "a=rtpmap:111 opus/48000/2\r\n" +
  395. "a=extmap:1 urn:ietf:params:rtp-hdrext:encrypt encrypted-duck-noises\r\n"
  396. XCTAssertEqual(expected, try VoIPCallSdpPatcher(.ENABLE_WITH_ONE_AND_TWO_BYTE_HEADER).patch(type: .LOCAL_OFFER, sdp: actual))
  397. expected = "v=0\r\n" +
  398. "m=audio 9 UDP/TLS/RTP/SAVPF 111\r\n" +
  399. "a=rtpmap:111 opus/48000/2\r\n" +
  400. "a=extmap:8 urn:ietf:params:rtp-hdrext:encrypt encrypted-duck-noises\r\n"
  401. XCTAssertEqual(expected, try VoIPCallSdpPatcher(.ENABLE_WITH_LEGACY_ONE_BYTE_HEADER_ONLY).patch(type: .LOCAL_ANSWER_OR_REMOTE_SDP, sdp: actual))
  402. expected = "v=0\r\n" +
  403. "m=audio 9 UDP/TLS/RTP/SAVPF 111\r\n" +
  404. "a=rtpmap:111 opus/48000/2\r\n" +
  405. "a=extmap:1 urn:ietf:params:rtp-hdrext:encrypt encrypted-duck-noises\r\n"
  406. XCTAssertEqual(expected, try VoIPCallSdpPatcher(.ENABLE_WITH_LEGACY_ONE_BYTE_HEADER_ONLY).patch(type: .LOCAL_OFFER, sdp: actual))
  407. }
  408. /// All encrypted RTP header extensions should be left as-is (apart from ID remapping).
  409. func testPatchWithRtpHeaderEncrypted() {
  410. let actual = "v=0\r\n" +
  411. "m=audio 9 UDP/TLS/RTP/SAVPF 111\r\n" +
  412. "a=rtpmap:111 opus/48000/2\r\n" +
  413. "a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid\r\n" +
  414. "a=extmap:5 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id\r\n" +
  415. "a=extmap:6 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id\r\n" +
  416. "a=extmap:7 duck-noises\r\n" +
  417. "a=extmap:8 urn:ietf:params:rtp-hdrext:encrypt encrypted-duck-noises\r\n"
  418. var expected = "v=0\r\n" +
  419. "m=audio 9 UDP/TLS/RTP/SAVPF 111\r\n" +
  420. "a=rtpmap:111 opus/48000/2\r\n" +
  421. "a=extmap:8 urn:ietf:params:rtp-hdrext:encrypt encrypted-duck-noises\r\n"
  422. XCTAssertEqual(expected, try VoIPCallSdpPatcher(.ENABLE_WITH_LEGACY_ONE_BYTE_HEADER_ONLY).patch(type: .LOCAL_ANSWER_OR_REMOTE_SDP, sdp: actual))
  423. expected = "v=0\r\n" +
  424. "m=audio 9 UDP/TLS/RTP/SAVPF 111\r\n" +
  425. "a=rtpmap:111 opus/48000/2\r\n" +
  426. "a=extmap:1 urn:ietf:params:rtp-hdrext:encrypt encrypted-duck-noises\r\n"
  427. XCTAssertEqual(expected, try VoIPCallSdpPatcher(.ENABLE_WITH_LEGACY_ONE_BYTE_HEADER_ONLY).patch(type: .LOCAL_OFFER, sdp: actual))
  428. }
  429. /// Audio only
  430. func testPatchWithLegacyAudioOnly() {
  431. let actual = "v=0\r\n" +
  432. "o=- 8329341859617817285 2 IN IP4 127.0.0.1\r\n" +
  433. "s=-\r\n" +
  434. "t=0 0\r\n" +
  435. "a=group:BUNDLE audio\r\n" +
  436. "a=extmap-allow-mixed\r\n" +
  437. "a=msid-semantic: WMS 3MACALL\r\n" +
  438. "m=audio 9 UDP/TLS/RTP/SAVPF 111 103 9 102 0 8 105 13 110 113 126\r\n" +
  439. "c=IN IP4 0.0.0.0\r\n" +
  440. "a=rtcp:9 IN IP4 0.0.0.0\r\n" +
  441. "a=ice-ufrag:hFGR\r\n" +
  442. "a=ice-pwd:HPszOFM6RDZWdhZ3PpPQ7w1H\r\n" +
  443. "a=ice-options:renomination\r\n" +
  444. "a=fingerprint:sha-256 F7:3A:7C:0C:A0:1E:EA:C5:2E:33:ED:90:61:55:0E:DF:59:8E:EA:EF:A6:E3:01:6E:A5:9E:34:78:5E:E3:8E:44\r\n" +
  445. "a=setup:active\r\n" +
  446. "a=mid:audio\r\n" +
  447. "a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n" +
  448. "a=extmap:2 urn:ietf:params:rtp-hdrext:csrc-audio-level\r\n" +
  449. "a=extmap:3 my-cool-extension-we-absolutely-want-to-have\r\n" +
  450. "a=extmap:5 urn:ietf:params:rtp-hdrext:encrypt urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n" +
  451. "a=extmap:6 urn:ietf:params:rtp-hdrext:encrypt urn:ietf:params:rtp-hdrext:csrc-audio-level\r\n" +
  452. "a=extmap:9 urn:ietf:params:rtp-hdrext:encrypt my-cool-extension-we-absolutely-want-to-have\r\n" +
  453. "a=sendrecv\r\n" +
  454. "a=rtcp-mux\r\n" +
  455. "a=rtpmap:111 opus/48000/2\r\n" +
  456. "a=rtcp-fb:111 transport-cc\r\n" +
  457. "a=fmtp:111 minptime=10;useinbandfec=1\r\n" +
  458. "a=rtpmap:103 ISAC/16000\r\n" +
  459. "a=rtpmap:9 G722/8000\r\n" +
  460. "a=rtpmap:102 ILBC/8000\r\n" +
  461. "a=rtpmap:0 PCMU/8000\r\n" +
  462. "a=rtpmap:8 PCMA/8000\r\n" +
  463. "a=rtpmap:105 CN/16000\r\n" +
  464. "a=rtpmap:13 CN/8000\r\n" +
  465. "a=rtpmap:110 telephone-event/48000\r\n" +
  466. "a=rtpmap:113 telephone-event/16000\r\n" +
  467. "a=rtpmap:126 telephone-event/8000\r\n" +
  468. "a=ssrc:2080079676 cname:Jb5aR24iJnFDp6OS\r\n" +
  469. "a=ssrc:2080079676 msid:3MACALL 3MACALLa0\r\n" +
  470. "a=ssrc:2080079676 mslabel:3MACALL\r\n" +
  471. "a=ssrc:2080079676 label:3MACALLa0\r\n"
  472. var expected = "v=0\r\n" +
  473. "o=- 8329341859617817285 2 IN IP4 127.0.0.1\r\n" +
  474. "s=-\r\n" +
  475. "t=0 0\r\n" +
  476. "a=group:BUNDLE audio\r\n" +
  477. "a=msid-semantic: WMS 3MACALL\r\n" +
  478. "m=audio 9 UDP/TLS/RTP/SAVPF 111\r\n" +
  479. "c=IN IP4 0.0.0.0\r\n" +
  480. "a=rtcp:9 IN IP4 0.0.0.0\r\n" +
  481. "a=ice-ufrag:hFGR\r\n" +
  482. "a=ice-pwd:HPszOFM6RDZWdhZ3PpPQ7w1H\r\n" +
  483. "a=ice-options:renomination\r\n" +
  484. "a=fingerprint:sha-256 F7:3A:7C:0C:A0:1E:EA:C5:2E:33:ED:90:61:55:0E:DF:59:8E:EA:EF:A6:E3:01:6E:A5:9E:34:78:5E:E3:8E:44\r\n" +
  485. "a=setup:active\r\n" +
  486. "a=mid:audio\r\n" +
  487. "a=sendrecv\r\n" +
  488. "a=rtcp-mux\r\n" +
  489. "a=rtpmap:111 opus/48000/2\r\n" +
  490. "a=rtcp-fb:111 transport-cc\r\n" +
  491. "a=fmtp:111 minptime=10;useinbandfec=1;stereo=0;sprop-stereo=0;cbr=1\r\n" +
  492. "a=ssrc:2080079676 cname:Jb5aR24iJnFDp6OS\r\n" +
  493. "a=ssrc:2080079676 msid:3MACALL 3MACALLa0\r\n" +
  494. "a=ssrc:2080079676 mslabel:3MACALL\r\n" +
  495. "a=ssrc:2080079676 label:3MACALLa0\r\n"
  496. XCTAssertEqual(expected, try VoIPCallSdpPatcher(.DISABLE).patch(type: .LOCAL_ANSWER_OR_REMOTE_SDP, sdp: actual))
  497. expected = "v=0\r\n" +
  498. "o=- 8329341859617817285 2 IN IP4 127.0.0.1\r\n" +
  499. "s=-\r\n" +
  500. "t=0 0\r\n" +
  501. "a=group:BUNDLE audio\r\n" +
  502. "a=msid-semantic: WMS 3MACALL\r\n" +
  503. "m=audio 9 UDP/TLS/RTP/SAVPF 111\r\n" +
  504. "c=IN IP4 0.0.0.0\r\n" +
  505. "a=rtcp:9 IN IP4 0.0.0.0\r\n" +
  506. "a=ice-ufrag:hFGR\r\n" +
  507. "a=ice-pwd:HPszOFM6RDZWdhZ3PpPQ7w1H\r\n" +
  508. "a=ice-options:renomination\r\n" +
  509. "a=fingerprint:sha-256 F7:3A:7C:0C:A0:1E:EA:C5:2E:33:ED:90:61:55:0E:DF:59:8E:EA:EF:A6:E3:01:6E:A5:9E:34:78:5E:E3:8E:44\r\n" +
  510. "a=setup:active\r\n" +
  511. "a=mid:audio\r\n" +
  512. "a=sendrecv\r\n" +
  513. "a=rtcp-mux\r\n" +
  514. "a=rtpmap:111 opus/48000/2\r\n" +
  515. "a=rtcp-fb:111 transport-cc\r\n" +
  516. "a=fmtp:111 minptime=10;useinbandfec=1;stereo=0;sprop-stereo=0;cbr=1\r\n" +
  517. "a=ssrc:2080079676 cname:Jb5aR24iJnFDp6OS\r\n" +
  518. "a=ssrc:2080079676 msid:3MACALL 3MACALLa0\r\n" +
  519. "a=ssrc:2080079676 mslabel:3MACALL\r\n" +
  520. "a=ssrc:2080079676 label:3MACALLa0\r\n"
  521. XCTAssertEqual(expected, try VoIPCallSdpPatcher(.DISABLE).patch(type: .LOCAL_OFFER, sdp: actual))
  522. }
  523. /// Video and audio
  524. func testPatchSdpWithAudioVideo() {
  525. let actual = "v=0\r\n" +
  526. "o=- 72507000979779968 2 IN IP4 127.0.0.1\r\n" +
  527. "s=-\r\n" +
  528. "t=0 0\r\n" +
  529. "a=group:BUNDLE 0 1 2\r\n" +
  530. "a=extmap-allow-mixed\r\n" +
  531. "a=msid-semantic: WMS 3MACALL\r\n" +
  532. "m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 102 0 8 106 105 13 110 112 113 126\r\n" +
  533. "c=IN IP4 0.0.0.0\r\n" +
  534. "a=rtcp:9 IN IP4 0.0.0.0\r\n" +
  535. "a=ice-ufrag:f30j\r\n" +
  536. "a=ice-pwd:G9GzFLlk1gthsg9uVhI3OyGv\r\n" +
  537. "a=ice-options:trickle renomination\r\n" +
  538. "a=fingerprint:sha-256 AE:86:73:4B:8A:55:BE:F1:2F:A2:8E:AA:98:8D:42:A4:D6:F8:2D:1C:CC:CD:12:C5:8E:14:BD:34:62:DA:35:8E\r\n" +
  539. "a=setup:actpass\r\n" +
  540. "a=mid:0\r\n" +
  541. "a=extmap:10 urn:ietf:params:rtp-hdrext:encrypt urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n" +
  542. "a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n" +
  543. "a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\n" +
  544. "a=extmap:16 urn:ietf:params:rtp-hdrext:encrypt http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r\n" +
  545. "a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r\n" +
  546. "a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid\r\n" +
  547. "a=extmap:5 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id\r\n" +
  548. "a=extmap:6 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id\r\n" +
  549. "a=extmap:15 urn:ietf:params:rtp-hdrext:encrypt http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\n" +
  550. "a=extmap:17 urn:ietf:params:rtp-hdrext:encrypt urn:ietf:params:rtp-hdrext:sdes:mid\r\n" +
  551. "a=extmap:18 urn:ietf:params:rtp-hdrext:encrypt urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id\r\n" +
  552. "a=extmap:19 urn:ietf:params:rtp-hdrext:encrypt urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id\r\n" +
  553. "a=sendrecv\r\n" +
  554. "a=msid:3MACALL 3MACALLa0\r\n" +
  555. "a=rtcp-mux\r\n" +
  556. "a=rtpmap:111 opus/48000/2\r\n" +
  557. "a=rtcp-fb:111 transport-cc\r\n" +
  558. "a=fmtp:111 minptime=10;useinbandfec=1\r\n" +
  559. "a=rtpmap:103 ISAC/16000\r\n" +
  560. "a=rtpmap:104 ISAC/32000\r\n" +
  561. "a=rtpmap:9 G722/8000\r\n" +
  562. "a=rtpmap:102 ILBC/8000\r\n" +
  563. "a=rtpmap:0 PCMU/8000\r\n" +
  564. "a=rtpmap:8 PCMA/8000\r\n" +
  565. "a=rtpmap:106 CN/32000\r\n" +
  566. "a=rtpmap:105 CN/16000\r\n" +
  567. "a=rtpmap:13 CN/8000\r\n" +
  568. "a=rtpmap:110 telephone-event/48000\r\n" +
  569. "a=rtpmap:112 telephone-event/32000\r\n" +
  570. "a=rtpmap:113 telephone-event/16000\r\n" +
  571. "a=rtpmap:126 telephone-event/8000\r\n" +
  572. "a=ssrc:3148626149 cname:xmp2nT2LrKeffKAn\r\n" +
  573. "a=ssrc:3148626149 msid:3MACALL 3MACALLa0\r\n" +
  574. "a=ssrc:3148626149 mslabel:3MACALL\r\n" +
  575. "a=ssrc:3148626149 label:3MACALLa0\r\n" +
  576. "m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 100 101 127 123 125\r\n" +
  577. "c=IN IP4 0.0.0.0\r\n" +
  578. "a=rtcp:9 IN IP4 0.0.0.0\r\n" +
  579. "a=ice-ufrag:f30j\r\n" +
  580. "a=ice-pwd:G9GzFLlk1gthsg9uVhI3OyGv\r\n" +
  581. "a=ice-options:trickle renomination\r\n" +
  582. "a=fingerprint:sha-256 AE:86:73:4B:8A:55:BE:F1:2F:A2:8E:AA:98:8D:42:A4:D6:F8:2D:1C:CC:CD:12:C5:8E:14:BD:34:62:DA:35:8E\r\n" +
  583. "a=setup:actpass\r\n" +
  584. "a=mid:1\r\n" +
  585. "a=extmap:25 urn:ietf:params:rtp-hdrext:encrypt http://tools.ietf.org/html/draft-ietf-avtext-framemarking-07\r\n" +
  586. "a=extmap:26 urn:ietf:params:rtp-hdrext:encrypt http://www.webrtc.org/experiments/rtp-hdrext/color-space\r\n" +
  587. "a=extmap:14 urn:ietf:params:rtp-hdrext:toffset\r\n" +
  588. "a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\n" +
  589. "a=extmap:13 urn:3gpp:video-orientation\r\n" +
  590. "a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r\n" +
  591. "a=extmap:12 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay\r\n" +
  592. "a=extmap:7 http://www.webrtc.org/experiments/rtp-hdrext/video-timing\r\n" +
  593. "a=extmap:17 urn:ietf:params:rtp-hdrext:encrypt urn:ietf:params:rtp-hdrext:sdes:mid\r\n" +
  594. "a=extmap:8 http://tools.ietf.org/html/draft-ietf-avtext-framemarking-07\r\n" +
  595. "a=extmap:9 http://www.webrtc.org/experiments/rtp-hdrext/color-space\r\n" +
  596. "a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid\r\n" +
  597. "a=extmap:5 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id\r\n" +
  598. "a=extmap:6 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id\r\n" +
  599. "a=extmap:20 urn:ietf:params:rtp-hdrext:encrypt urn:ietf:params:rtp-hdrext:toffset\r\n" +
  600. "a=extmap:15 urn:ietf:params:rtp-hdrext:encrypt http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\n" +
  601. "a=extmap:21 urn:ietf:params:rtp-hdrext:encrypt urn:3gpp:video-orientation\r\n" +
  602. "a=extmap:16 urn:ietf:params:rtp-hdrext:encrypt http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r\n" +
  603. "a=extmap:22 urn:ietf:params:rtp-hdrext:encrypt http://www.webrtc.org/experiments/rtp-hdrext/playout-delay\r\n" +
  604. "a=extmap:23 urn:ietf:params:rtp-hdrext:encrypt http://www.webrtc.org/experiments/rtp-hdrext/video-content-type\r\n" +
  605. "a=extmap:24 urn:ietf:params:rtp-hdrext:encrypt http://www.webrtc.org/experiments/rtp-hdrext/video-timing\r\n" +
  606. "a=extmap:11 http://www.webrtc.org/experiments/rtp-hdrext/video-content-type\r\n" +
  607. "a=extmap:18 urn:ietf:params:rtp-hdrext:encrypt urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id\r\n" +
  608. "a=extmap:19 urn:ietf:params:rtp-hdrext:encrypt urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id\r\n" +
  609. "a=sendrecv\r\n" +
  610. "a=msid:3MACALL 3MACALLv0\r\n" +
  611. "a=rtcp-mux\r\n" +
  612. "a=rtcp-rsize\r\n" +
  613. "a=rtpmap:96 VP8/90000\r\n" +
  614. "a=rtcp-fb:96 goog-remb\r\n" +
  615. "a=rtcp-fb:96 transport-cc\r\n" +
  616. "a=rtcp-fb:96 ccm fir\r\n" +
  617. "a=rtcp-fb:96 nack\r\n" +
  618. "a=rtcp-fb:96 nack pli\r\n" +
  619. "a=rtpmap:97 rtx/90000\r\n" +
  620. "a=fmtp:97 apt=96\r\n" +
  621. "a=rtpmap:98 VP9/90000\r\n" +
  622. "a=rtcp-fb:98 goog-remb\r\n" +
  623. "a=rtcp-fb:98 transport-cc\r\n" +
  624. "a=rtcp-fb:98 ccm fir\r\n" +
  625. "a=rtcp-fb:98 nack\r\n" +
  626. "a=rtcp-fb:98 nack pli\r\n" +
  627. "a=rtpmap:99 rtx/90000\r\n" +
  628. "a=fmtp:99 apt=98\r\n" +
  629. "a=rtpmap:100 H264/90000\r\n" +
  630. "a=rtcp-fb:100 goog-remb\r\n" +
  631. "a=rtcp-fb:100 transport-cc\r\n" +
  632. "a=rtcp-fb:100 ccm fir\r\n" +
  633. "a=rtcp-fb:100 nack\r\n" +
  634. "a=rtcp-fb:100 nack pli\r\n" +
  635. "a=fmtp:100 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f\r\n" +
  636. "a=rtpmap:101 rtx/90000\r\n" +
  637. "a=fmtp:101 apt=100\r\n" +
  638. "a=rtpmap:127 red/90000\r\n" +
  639. "a=rtpmap:123 rtx/90000\r\n" +
  640. "a=fmtp:123 apt=127\r\n" +
  641. "a=rtpmap:125 ulpfec/90000\r\n" +
  642. "a=ssrc-group:FID 2961420724 927121398\r\n" +
  643. "a=ssrc:2961420724 cname:xmp2nT2LrKeffKAn\r\n" +
  644. "a=ssrc:2961420724 msid:3MACALL 3MACALLv0\r\n" +
  645. "a=ssrc:2961420724 mslabel:3MACALL\r\n" +
  646. "a=ssrc:2961420724 label:3MACALLv0\r\n" +
  647. "a=ssrc:927121398 cname:xmp2nT2LrKeffKAn\r\n" +
  648. "a=ssrc:927121398 msid:3MACALL 3MACALLv0\r\n" +
  649. "a=ssrc:927121398 mslabel:3MACALL\r\n" +
  650. "a=ssrc:927121398 label:3MACALLv0\r\n" +
  651. "m=application 9 UDP/DTLS/SCTP webrtc-datachannel\r\n" +
  652. "c=IN IP4 0.0.0.0\r\n" +
  653. "a=ice-ufrag:f30j\r\n" +
  654. "a=ice-pwd:G9GzFLlk1gthsg9uVhI3OyGv\r\n" +
  655. "a=ice-options:trickle renomination\r\n" +
  656. "a=fingerprint:sha-256 AE:86:73:4B:8A:55:BE:F1:2F:A2:8E:AA:98:8D:42:A4:D6:F8:2D:1C:CC:CD:12:C5:8E:14:BD:34:62:DA:35:8E\r\n" +
  657. "a=setup:actpass\r\n" +
  658. "a=mid:2\r\n" +
  659. "a=sctp-port:5000\r\n" +
  660. "a=max-message-size:262144\r\n"
  661. var expected = "v=0\r\n" +
  662. "o=- 72507000979779968 2 IN IP4 127.0.0.1\r\n" +
  663. "s=-\r\n" +
  664. "t=0 0\r\n" +
  665. "a=group:BUNDLE 0 1 2\r\n" +
  666. "a=extmap-allow-mixed\r\n" +
  667. "a=msid-semantic: WMS 3MACALL\r\n" +
  668. "m=audio 9 UDP/TLS/RTP/SAVPF 111\r\n" +
  669. "c=IN IP4 0.0.0.0\r\n" +
  670. "a=rtcp:9 IN IP4 0.0.0.0\r\n" +
  671. "a=ice-ufrag:f30j\r\n" +
  672. "a=ice-pwd:G9GzFLlk1gthsg9uVhI3OyGv\r\n" +
  673. "a=ice-options:trickle renomination\r\n" +
  674. "a=fingerprint:sha-256 AE:86:73:4B:8A:55:BE:F1:2F:A2:8E:AA:98:8D:42:A4:D6:F8:2D:1C:CC:CD:12:C5:8E:14:BD:34:62:DA:35:8E\r\n" +
  675. "a=setup:actpass\r\n" +
  676. "a=mid:0\r\n" +
  677. "a=extmap:16 urn:ietf:params:rtp-hdrext:encrypt http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r\n" +
  678. "a=extmap:15 urn:ietf:params:rtp-hdrext:encrypt http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\n" +
  679. "a=extmap:17 urn:ietf:params:rtp-hdrext:encrypt urn:ietf:params:rtp-hdrext:sdes:mid\r\n" +
  680. "a=extmap:18 urn:ietf:params:rtp-hdrext:encrypt urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id\r\n" +
  681. "a=extmap:19 urn:ietf:params:rtp-hdrext:encrypt urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id\r\n" +
  682. "a=sendrecv\r\n" +
  683. "a=msid:3MACALL 3MACALLa0\r\n" +
  684. "a=rtcp-mux\r\n" +
  685. "a=rtpmap:111 opus/48000/2\r\n" +
  686. "a=rtcp-fb:111 transport-cc\r\n" +
  687. "a=fmtp:111 minptime=10;useinbandfec=1;stereo=0;sprop-stereo=0;cbr=1\r\n" +
  688. "a=ssrc:3148626149 cname:xmp2nT2LrKeffKAn\r\n" +
  689. "a=ssrc:3148626149 msid:3MACALL 3MACALLa0\r\n" +
  690. "a=ssrc:3148626149 mslabel:3MACALL\r\n" +
  691. "a=ssrc:3148626149 label:3MACALLa0\r\n" +
  692. "m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 100 101 127 123 125\r\n" +
  693. "c=IN IP4 0.0.0.0\r\n" +
  694. "a=rtcp:9 IN IP4 0.0.0.0\r\n" +
  695. "a=ice-ufrag:f30j\r\n" +
  696. "a=ice-pwd:G9GzFLlk1gthsg9uVhI3OyGv\r\n" +
  697. "a=ice-options:trickle renomination\r\n" +
  698. "a=fingerprint:sha-256 AE:86:73:4B:8A:55:BE:F1:2F:A2:8E:AA:98:8D:42:A4:D6:F8:2D:1C:CC:CD:12:C5:8E:14:BD:34:62:DA:35:8E\r\n" +
  699. "a=setup:actpass\r\n" +
  700. "a=mid:1\r\n" +
  701. "a=extmap:26 urn:ietf:params:rtp-hdrext:encrypt http://www.webrtc.org/experiments/rtp-hdrext/color-space\r\n" +
  702. "a=extmap:17 urn:ietf:params:rtp-hdrext:encrypt urn:ietf:params:rtp-hdrext:sdes:mid\r\n" +
  703. "a=extmap:20 urn:ietf:params:rtp-hdrext:encrypt urn:ietf:params:rtp-hdrext:toffset\r\n" +
  704. "a=extmap:15 urn:ietf:params:rtp-hdrext:encrypt http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\n" +
  705. "a=extmap:21 urn:ietf:params:rtp-hdrext:encrypt urn:3gpp:video-orientation\r\n" +
  706. "a=extmap:16 urn:ietf:params:rtp-hdrext:encrypt http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r\n" +
  707. "a=extmap:22 urn:ietf:params:rtp-hdrext:encrypt http://www.webrtc.org/experiments/rtp-hdrext/playout-delay\r\n" +
  708. "a=extmap:23 urn:ietf:params:rtp-hdrext:encrypt http://www.webrtc.org/experiments/rtp-hdrext/video-content-type\r\n" +
  709. "a=extmap:24 urn:ietf:params:rtp-hdrext:encrypt http://www.webrtc.org/experiments/rtp-hdrext/video-timing\r\n" +
  710. "a=extmap:18 urn:ietf:params:rtp-hdrext:encrypt urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id\r\n" +
  711. "a=extmap:19 urn:ietf:params:rtp-hdrext:encrypt urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id\r\n" +
  712. "a=sendrecv\r\n" +
  713. "a=msid:3MACALL 3MACALLv0\r\n" +
  714. "a=rtcp-mux\r\n" +
  715. "a=rtcp-rsize\r\n" +
  716. "a=rtpmap:96 VP8/90000\r\n" +
  717. "a=rtcp-fb:96 goog-remb\r\n" +
  718. "a=rtcp-fb:96 transport-cc\r\n" +
  719. "a=rtcp-fb:96 ccm fir\r\n" +
  720. "a=rtcp-fb:96 nack\r\n" +
  721. "a=rtcp-fb:96 nack pli\r\n" +
  722. "a=rtpmap:97 rtx/90000\r\n" +
  723. "a=fmtp:97 apt=96\r\n" +
  724. "a=rtpmap:98 VP9/90000\r\n" +
  725. "a=rtcp-fb:98 goog-remb\r\n" +
  726. "a=rtcp-fb:98 transport-cc\r\n" +
  727. "a=rtcp-fb:98 ccm fir\r\n" +
  728. "a=rtcp-fb:98 nack\r\n" +
  729. "a=rtcp-fb:98 nack pli\r\n" +
  730. "a=rtpmap:99 rtx/90000\r\n" +
  731. "a=fmtp:99 apt=98\r\n" +
  732. "a=rtpmap:100 H264/90000\r\n" +
  733. "a=rtcp-fb:100 goog-remb\r\n" +
  734. "a=rtcp-fb:100 transport-cc\r\n" +
  735. "a=rtcp-fb:100 ccm fir\r\n" +
  736. "a=rtcp-fb:100 nack\r\n" +
  737. "a=rtcp-fb:100 nack pli\r\n" +
  738. "a=fmtp:100 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f\r\n" +
  739. "a=rtpmap:101 rtx/90000\r\n" +
  740. "a=fmtp:101 apt=100\r\n" +
  741. "a=rtpmap:127 red/90000\r\n" +
  742. "a=rtpmap:123 rtx/90000\r\n" +
  743. "a=fmtp:123 apt=127\r\n" +
  744. "a=rtpmap:125 ulpfec/90000\r\n" +
  745. "a=ssrc-group:FID 2961420724 927121398\r\n" +
  746. "a=ssrc:2961420724 cname:xmp2nT2LrKeffKAn\r\n" +
  747. "a=ssrc:2961420724 msid:3MACALL 3MACALLv0\r\n" +
  748. "a=ssrc:2961420724 mslabel:3MACALL\r\n" +
  749. "a=ssrc:2961420724 label:3MACALLv0\r\n" +
  750. "a=ssrc:927121398 cname:xmp2nT2LrKeffKAn\r\n" +
  751. "a=ssrc:927121398 msid:3MACALL 3MACALLv0\r\n" +
  752. "a=ssrc:927121398 mslabel:3MACALL\r\n" +
  753. "a=ssrc:927121398 label:3MACALLv0\r\n" +
  754. "m=application 9 UDP/DTLS/SCTP webrtc-datachannel\r\n" +
  755. "c=IN IP4 0.0.0.0\r\n" +
  756. "a=ice-ufrag:f30j\r\n" +
  757. "a=ice-pwd:G9GzFLlk1gthsg9uVhI3OyGv\r\n" +
  758. "a=ice-options:trickle renomination\r\n" +
  759. "a=fingerprint:sha-256 AE:86:73:4B:8A:55:BE:F1:2F:A2:8E:AA:98:8D:42:A4:D6:F8:2D:1C:CC:CD:12:C5:8E:14:BD:34:62:DA:35:8E\r\n" +
  760. "a=setup:actpass\r\n" +
  761. "a=mid:2\r\n" +
  762. "a=sctp-port:5000\r\n" +
  763. "a=max-message-size:262144\r\n"
  764. XCTAssertEqual(expected, try VoIPCallSdpPatcher(.ENABLE_WITH_ONE_AND_TWO_BYTE_HEADER).patch(type: .LOCAL_ANSWER_OR_REMOTE_SDP, sdp: actual))
  765. expected = "v=0\r\n" +
  766. "o=- 72507000979779968 2 IN IP4 127.0.0.1\r\n" +
  767. "s=-\r\n" +
  768. "t=0 0\r\n" +
  769. "a=group:BUNDLE 0 1 2\r\n" +
  770. "a=extmap-allow-mixed\r\n" +
  771. "a=msid-semantic: WMS 3MACALL\r\n" +
  772. "m=audio 9 UDP/TLS/RTP/SAVPF 111\r\n" +
  773. "c=IN IP4 0.0.0.0\r\n" +
  774. "a=rtcp:9 IN IP4 0.0.0.0\r\n" +
  775. "a=ice-ufrag:f30j\r\n" +
  776. "a=ice-pwd:G9GzFLlk1gthsg9uVhI3OyGv\r\n" +
  777. "a=ice-options:trickle renomination\r\n" +
  778. "a=fingerprint:sha-256 AE:86:73:4B:8A:55:BE:F1:2F:A2:8E:AA:98:8D:42:A4:D6:F8:2D:1C:CC:CD:12:C5:8E:14:BD:34:62:DA:35:8E\r\n" +
  779. "a=setup:actpass\r\n" +
  780. "a=mid:0\r\n" +
  781. "a=extmap:1 urn:ietf:params:rtp-hdrext:encrypt http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r\n" +
  782. "a=extmap:2 urn:ietf:params:rtp-hdrext:encrypt http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\n" +
  783. "a=extmap:3 urn:ietf:params:rtp-hdrext:encrypt urn:ietf:params:rtp-hdrext:sdes:mid\r\n" +
  784. "a=extmap:4 urn:ietf:params:rtp-hdrext:encrypt urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id\r\n" +
  785. "a=extmap:5 urn:ietf:params:rtp-hdrext:encrypt urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id\r\n" +
  786. "a=sendrecv\r\n" +
  787. "a=msid:3MACALL 3MACALLa0\r\n" +
  788. "a=rtcp-mux\r\n" +
  789. "a=rtpmap:111 opus/48000/2\r\n" +
  790. "a=rtcp-fb:111 transport-cc\r\n" +
  791. "a=fmtp:111 minptime=10;useinbandfec=1;stereo=0;sprop-stereo=0;cbr=1\r\n" +
  792. "a=ssrc:3148626149 cname:xmp2nT2LrKeffKAn\r\n" +
  793. "a=ssrc:3148626149 msid:3MACALL 3MACALLa0\r\n" +
  794. "a=ssrc:3148626149 mslabel:3MACALL\r\n" +
  795. "a=ssrc:3148626149 label:3MACALLa0\r\n" +
  796. "m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 100 101 127 123 125\r\n" +
  797. "c=IN IP4 0.0.0.0\r\n" +
  798. "a=rtcp:9 IN IP4 0.0.0.0\r\n" +
  799. "a=ice-ufrag:f30j\r\n" +
  800. "a=ice-pwd:G9GzFLlk1gthsg9uVhI3OyGv\r\n" +
  801. "a=ice-options:trickle renomination\r\n" +
  802. "a=fingerprint:sha-256 AE:86:73:4B:8A:55:BE:F1:2F:A2:8E:AA:98:8D:42:A4:D6:F8:2D:1C:CC:CD:12:C5:8E:14:BD:34:62:DA:35:8E\r\n" +
  803. "a=setup:actpass\r\n" +
  804. "a=mid:1\r\n" +
  805. "a=extmap:6 urn:ietf:params:rtp-hdrext:encrypt http://www.webrtc.org/experiments/rtp-hdrext/color-space\r\n" +
  806. "a=extmap:3 urn:ietf:params:rtp-hdrext:encrypt urn:ietf:params:rtp-hdrext:sdes:mid\r\n" +
  807. "a=extmap:7 urn:ietf:params:rtp-hdrext:encrypt urn:ietf:params:rtp-hdrext:toffset\r\n" +
  808. "a=extmap:2 urn:ietf:params:rtp-hdrext:encrypt http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\n" +
  809. "a=extmap:8 urn:ietf:params:rtp-hdrext:encrypt urn:3gpp:video-orientation\r\n" +
  810. "a=extmap:1 urn:ietf:params:rtp-hdrext:encrypt http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r\n" +
  811. "a=extmap:9 urn:ietf:params:rtp-hdrext:encrypt http://www.webrtc.org/experiments/rtp-hdrext/playout-delay\r\n" +
  812. "a=extmap:10 urn:ietf:params:rtp-hdrext:encrypt http://www.webrtc.org/experiments/rtp-hdrext/video-content-type\r\n" +
  813. "a=extmap:11 urn:ietf:params:rtp-hdrext:encrypt http://www.webrtc.org/experiments/rtp-hdrext/video-timing\r\n" +
  814. "a=extmap:4 urn:ietf:params:rtp-hdrext:encrypt urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id\r\n" +
  815. "a=extmap:5 urn:ietf:params:rtp-hdrext:encrypt urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id\r\n" +
  816. "a=sendrecv\r\n" +
  817. "a=msid:3MACALL 3MACALLv0\r\n" +
  818. "a=rtcp-mux\r\n" +
  819. "a=rtcp-rsize\r\n" +
  820. "a=rtpmap:96 VP8/90000\r\n" +
  821. "a=rtcp-fb:96 goog-remb\r\n" +
  822. "a=rtcp-fb:96 transport-cc\r\n" +
  823. "a=rtcp-fb:96 ccm fir\r\n" +
  824. "a=rtcp-fb:96 nack\r\n" +
  825. "a=rtcp-fb:96 nack pli\r\n" +
  826. "a=rtpmap:97 rtx/90000\r\n" +
  827. "a=fmtp:97 apt=96\r\n" +
  828. "a=rtpmap:98 VP9/90000\r\n" +
  829. "a=rtcp-fb:98 goog-remb\r\n" +
  830. "a=rtcp-fb:98 transport-cc\r\n" +
  831. "a=rtcp-fb:98 ccm fir\r\n" +
  832. "a=rtcp-fb:98 nack\r\n" +
  833. "a=rtcp-fb:98 nack pli\r\n" +
  834. "a=rtpmap:99 rtx/90000\r\n" +
  835. "a=fmtp:99 apt=98\r\n" +
  836. "a=rtpmap:100 H264/90000\r\n" +
  837. "a=rtcp-fb:100 goog-remb\r\n" +
  838. "a=rtcp-fb:100 transport-cc\r\n" +
  839. "a=rtcp-fb:100 ccm fir\r\n" +
  840. "a=rtcp-fb:100 nack\r\n" +
  841. "a=rtcp-fb:100 nack pli\r\n" +
  842. "a=fmtp:100 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f\r\n" +
  843. "a=rtpmap:101 rtx/90000\r\n" +
  844. "a=fmtp:101 apt=100\r\n" +
  845. "a=rtpmap:127 red/90000\r\n" +
  846. "a=rtpmap:123 rtx/90000\r\n" +
  847. "a=fmtp:123 apt=127\r\n" +
  848. "a=rtpmap:125 ulpfec/90000\r\n" +
  849. "a=ssrc-group:FID 2961420724 927121398\r\n" +
  850. "a=ssrc:2961420724 cname:xmp2nT2LrKeffKAn\r\n" +
  851. "a=ssrc:2961420724 msid:3MACALL 3MACALLv0\r\n" +
  852. "a=ssrc:2961420724 mslabel:3MACALL\r\n" +
  853. "a=ssrc:2961420724 label:3MACALLv0\r\n" +
  854. "a=ssrc:927121398 cname:xmp2nT2LrKeffKAn\r\n" +
  855. "a=ssrc:927121398 msid:3MACALL 3MACALLv0\r\n" +
  856. "a=ssrc:927121398 mslabel:3MACALL\r\n" +
  857. "a=ssrc:927121398 label:3MACALLv0\r\n" +
  858. "m=application 9 UDP/DTLS/SCTP webrtc-datachannel\r\n" +
  859. "c=IN IP4 0.0.0.0\r\n" +
  860. "a=ice-ufrag:f30j\r\n" +
  861. "a=ice-pwd:G9GzFLlk1gthsg9uVhI3OyGv\r\n" +
  862. "a=ice-options:trickle renomination\r\n" +
  863. "a=fingerprint:sha-256 AE:86:73:4B:8A:55:BE:F1:2F:A2:8E:AA:98:8D:42:A4:D6:F8:2D:1C:CC:CD:12:C5:8E:14:BD:34:62:DA:35:8E\r\n" +
  864. "a=setup:actpass\r\n" +
  865. "a=mid:2\r\n" +
  866. "a=sctp-port:5000\r\n" +
  867. "a=max-message-size:262144\r\n"
  868. XCTAssertEqual(expected, try VoIPCallSdpPatcher(.ENABLE_WITH_ONE_AND_TWO_BYTE_HEADER).patch(type: .LOCAL_OFFER, sdp: actual))
  869. }
  870. }