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