1/** 2 * Copyright 2016 The Chromium Authors. All rights reserved. 3 * Use of this source code is governed by a BSD-style license that can be 4 * found in the LICENSE file. 5 */ 6 7// This file was copied from: 8// https://cs.chromium.org/chromium/src/chrome/test/data/webrtc/munge_sdp.js. 9 10/** 11 * See |setSdpDefaultCodec|. 12 */ 13function setSdpDefaultVideoCodec(sdp, codec) { 14 return setSdpDefaultCodec(sdp, 'video', codec); 15} 16 17/** 18 * Returns a modified version of |sdp| where the |codec| has been promoted to be 19 * the default codec, i.e. the codec whose ID is first in the list of codecs on 20 * the 'm=|type|' line, where |type| is 'audio' or 'video'. 21 * @private 22 */ 23function setSdpDefaultCodec(sdp, type, codec) { 24 var sdpLines = splitSdpLines(sdp); 25 26 // Find codec ID, e.g. 100 for 'VP8' if 'a=rtpmap:100 VP8/9000'. 27 var codecId = findRtpmapId(sdpLines, codec); 28 if (codecId === null) { 29 failure('sdpPreferCodec', 30 'Missing a=rtpmap entry for |codec| = ' + codec + ' in ' + sdp); 31 } 32 33 // Find 'm=|type|' line, e.g. 'm=video 9 UDP/TLS/RTP/SAVPF 100 101 107 116'. 34 var mLineNo = findLine(sdpLines, 'm=' + type); 35 if (mLineNo === null) { 36 failure('setSdpDefaultCodec', 37 '\'m=' + type + '\' line missing from |sdp|.'); 38 } 39 40 // Modify video line to use the desired codec as the default. 41 sdpLines[mLineNo] = setMLineDefaultCodec(sdpLines[mLineNo], codecId); 42 return mergeSdpLines(sdpLines); 43} 44 45/** 46 * * See |getSdpDefaultCodec|. 47 * */ 48function getSdpDefaultVideoCodec(sdp) { 49 return getSdpDefaultCodec(sdp, 'video'); 50} 51 52/** 53 * Gets the default codec according to the |sdp|, i.e. the name of the codec 54 * whose ID is first in the list of codecs on the 'm=|type|' line, where |type| 55 * is 'audio' or 'video'. 56 * @private 57 */ 58function getSdpDefaultCodec(sdp, type) { 59 var sdpLines = splitSdpLines(sdp); 60 61 // Find 'm=|type|' line, e.g. 'm=video 9 UDP/TLS/RTP/SAVPF 100 101 107 116'. 62 var mLineNo = findLine(sdpLines, 'm=' + type); 63 if (mLineNo === null) { 64 failure('getSdpDefaultCodec', 65 '\'m=' + type + '\' line missing from |sdp|.'); 66 } 67 68 // The default codec's ID. 69 var defaultCodecId = getMLineDefaultCodec(sdpLines[mLineNo]); 70 if (defaultCodecId === null) { 71 failure('getSdpDefaultCodec', 72 '\'m=' + type + '\' line contains no codecs.'); 73 } 74 75 // Find codec name, e.g. 'VP8' for 100 if 'a=rtpmap:100 VP8/9000'. 76 var defaultCodec = findRtpmapCodec(sdpLines, defaultCodecId); 77 if (defaultCodec === null) { 78 failure('getSdpDefaultCodec', 79 'Unknown codec name for default codec ' + defaultCodecId + '.'); 80 } 81 return defaultCodec; 82} 83 84/** 85 * Searches through all |sdpLines| for the 'a=rtpmap:' line for the codec of 86 * the specified name, returning its ID as an int if found, or null otherwise. 87 * |codec| is the case-sensitive name of the codec. 88 * For example, if |sdpLines| contains 'a=rtpmap:100 VP8/9000' and |codec| is 89 * 'VP8', this function returns 100. 90 * @private 91 */ 92function findRtpmapId(sdpLines, codec) { 93 var lineNo = findRtpmapLine(sdpLines, codec); 94 if (lineNo === null) 95 return null; 96 // Parse <id> from 'a=rtpmap:<id> <codec>/<rate>'. 97 var id = sdpLines[lineNo].substring(9, sdpLines[lineNo].indexOf(' ')); 98 return parseInt(id); 99} 100 101/** 102 * Searches through all |sdpLines| for the 'a=rtpmap:' line for the codec of 103 * the specified codec ID, returning its name if found, or null otherwise. 104 * For example, if |sdpLines| contains 'a=rtpmap:100 VP8/9000' and |id| is 100, 105 * this function returns 'VP8'. 106 * @private 107 */ 108function findRtpmapCodec(sdpLines, id) { 109 var lineNo = findRtpmapLine(sdpLines, id); 110 if (lineNo === null) 111 return null; 112 // Parse <codec> from 'a=rtpmap:<id> <codec>/<rate>'. 113 var from = sdpLines[lineNo].indexOf(' '); 114 var to = sdpLines[lineNo].indexOf('/', from); 115 if (from === null || to === null || from + 1 >= to) 116 failure('findRtpmapCodec', ''); 117 return sdpLines[lineNo].substring(from + 1, to); 118} 119 120/** 121 * Finds the first 'a=rtpmap:' line from |sdpLines| that contains |contains| and 122 * returns its line index, or null if no such line was found. |contains| may be 123 * the codec ID, codec name or bitrate. An 'a=rtpmap:' line looks like this: 124 * 'a=rtpmap:<id> <codec>/<rate>'. 125 */ 126function findRtpmapLine(sdpLines, contains) { 127 for (var i = 0; i < sdpLines.length; i++) { 128 // Is 'a=rtpmap:' line containing |contains| string? 129 if (sdpLines[i].startsWith('a=rtpmap:') && 130 sdpLines[i].indexOf(contains) != -1) { 131 // Expecting pattern 'a=rtpmap:<id> <codec>/<rate>'. 132 var pattern = new RegExp('a=rtpmap:(\\d+) \\w+\\/\\d+'); 133 if (!sdpLines[i].match(pattern)) 134 failure('findRtpmapLine', 'Unexpected "a=rtpmap:" pattern.'); 135 // Return line index. 136 return i; 137 } 138 } 139 return null; 140} 141 142/** 143 * Returns a modified version of |mLine| that has |codecId| first in the list of 144 * codec IDs. For example, setMLineDefaultCodec( 145 * 'm=video 9 UDP/TLS/RTP/SAVPF 100 101 107 116 117 96', 107) 146 * Returns: 147 * 'm=video 9 UDP/TLS/RTP/SAVPF 107 100 101 116 117 96' 148 * @private 149 */ 150function setMLineDefaultCodec(mLine, codecId) { 151 var elements = mLine.split(' '); 152 153 // Copy first three elements, codec order starts on fourth. 154 var newLine = elements.slice(0, 3); 155 156 // Put target |codecId| first and copy the rest. 157 newLine.push(codecId); 158 for (var i = 3; i < elements.length; i++) { 159 if (elements[i] != codecId) 160 newLine.push(elements[i]); 161 } 162 163 return newLine.join(' '); 164} 165 166/** 167 * Returns the default codec's ID from the |mLine|, or null if the codec list is 168 * empty. The default codec is the codec whose ID is first in the list of codec 169 * IDs on the |mLine|. For example, getMLineDefaultCodec( 170 * 'm=video 9 UDP/TLS/RTP/SAVPF 100 101 107 116 117 96') 171 * Returns: 172 * 100 173 * @private 174 */ 175function getMLineDefaultCodec(mLine) { 176 var elements = mLine.split(' '); 177 if (elements.length < 4) 178 return null; 179 return parseInt(elements[3]); 180} 181 182/** @private */ 183function splitSdpLines(sdp) { 184 return sdp.split('\r\n'); 185} 186 187/** @private */ 188function mergeSdpLines(sdpLines) { 189 return sdpLines.join('\r\n'); 190} 191 192/** @private */ 193function findLine(lines, startsWith) { 194 for (var i = 0; i < lines.length; i++) { 195 if (lines[i].startsWith(startsWith)) 196 return i; 197 } 198 return null; 199} 200