/** * Copyright 2016 The Chromium Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ // This file was copied from: // https://cs.chromium.org/chromium/src/chrome/test/data/webrtc/munge_sdp.js. /** * See |setSdpDefaultCodec|. */ function setSdpDefaultVideoCodec(sdp, codec) { return setSdpDefaultCodec(sdp, 'video', codec); } /** * Returns a modified version of |sdp| where the |codec| has been promoted to be * the default codec, i.e. the codec whose ID is first in the list of codecs on * the 'm=|type|' line, where |type| is 'audio' or 'video'. * @private */ function setSdpDefaultCodec(sdp, type, codec) { var sdpLines = splitSdpLines(sdp); // Find codec ID, e.g. 100 for 'VP8' if 'a=rtpmap:100 VP8/9000'. var codecId = findRtpmapId(sdpLines, codec); if (codecId === null) { failure('sdpPreferCodec', 'Missing a=rtpmap entry for |codec| = ' + codec + ' in ' + sdp); } // Find 'm=|type|' line, e.g. 'm=video 9 UDP/TLS/RTP/SAVPF 100 101 107 116'. var mLineNo = findLine(sdpLines, 'm=' + type); if (mLineNo === null) { failure('setSdpDefaultCodec', '\'m=' + type + '\' line missing from |sdp|.'); } // Modify video line to use the desired codec as the default. sdpLines[mLineNo] = setMLineDefaultCodec(sdpLines[mLineNo], codecId); return mergeSdpLines(sdpLines); } /** * * See |getSdpDefaultCodec|. * */ function getSdpDefaultVideoCodec(sdp) { return getSdpDefaultCodec(sdp, 'video'); } /** * Gets the default codec according to the |sdp|, i.e. the name of the codec * whose ID is first in the list of codecs on the 'm=|type|' line, where |type| * is 'audio' or 'video'. * @private */ function getSdpDefaultCodec(sdp, type) { var sdpLines = splitSdpLines(sdp); // Find 'm=|type|' line, e.g. 'm=video 9 UDP/TLS/RTP/SAVPF 100 101 107 116'. var mLineNo = findLine(sdpLines, 'm=' + type); if (mLineNo === null) { failure('getSdpDefaultCodec', '\'m=' + type + '\' line missing from |sdp|.'); } // The default codec's ID. var defaultCodecId = getMLineDefaultCodec(sdpLines[mLineNo]); if (defaultCodecId === null) { failure('getSdpDefaultCodec', '\'m=' + type + '\' line contains no codecs.'); } // Find codec name, e.g. 'VP8' for 100 if 'a=rtpmap:100 VP8/9000'. var defaultCodec = findRtpmapCodec(sdpLines, defaultCodecId); if (defaultCodec === null) { failure('getSdpDefaultCodec', 'Unknown codec name for default codec ' + defaultCodecId + '.'); } return defaultCodec; } /** * Searches through all |sdpLines| for the 'a=rtpmap:' line for the codec of * the specified name, returning its ID as an int if found, or null otherwise. * |codec| is the case-sensitive name of the codec. * For example, if |sdpLines| contains 'a=rtpmap:100 VP8/9000' and |codec| is * 'VP8', this function returns 100. * @private */ function findRtpmapId(sdpLines, codec) { var lineNo = findRtpmapLine(sdpLines, codec); if (lineNo === null) return null; // Parse from 'a=rtpmap: /'. var id = sdpLines[lineNo].substring(9, sdpLines[lineNo].indexOf(' ')); return parseInt(id); } /** * Searches through all |sdpLines| for the 'a=rtpmap:' line for the codec of * the specified codec ID, returning its name if found, or null otherwise. * For example, if |sdpLines| contains 'a=rtpmap:100 VP8/9000' and |id| is 100, * this function returns 'VP8'. * @private */ function findRtpmapCodec(sdpLines, id) { var lineNo = findRtpmapLine(sdpLines, id); if (lineNo === null) return null; // Parse from 'a=rtpmap: /'. var from = sdpLines[lineNo].indexOf(' '); var to = sdpLines[lineNo].indexOf('/', from); if (from === null || to === null || from + 1 >= to) failure('findRtpmapCodec', ''); return sdpLines[lineNo].substring(from + 1, to); } /** * Finds the first 'a=rtpmap:' line from |sdpLines| that contains |contains| and * returns its line index, or null if no such line was found. |contains| may be * the codec ID, codec name or bitrate. An 'a=rtpmap:' line looks like this: * 'a=rtpmap: /'. */ function findRtpmapLine(sdpLines, contains) { for (var i = 0; i < sdpLines.length; i++) { // Is 'a=rtpmap:' line containing |contains| string? if (sdpLines[i].startsWith('a=rtpmap:') && sdpLines[i].indexOf(contains) != -1) { // Expecting pattern 'a=rtpmap: /'. var pattern = new RegExp('a=rtpmap:(\\d+) \\w+\\/\\d+'); if (!sdpLines[i].match(pattern)) failure('findRtpmapLine', 'Unexpected "a=rtpmap:" pattern.'); // Return line index. return i; } } return null; } /** * Returns a modified version of |mLine| that has |codecId| first in the list of * codec IDs. For example, setMLineDefaultCodec( * 'm=video 9 UDP/TLS/RTP/SAVPF 100 101 107 116 117 96', 107) * Returns: * 'm=video 9 UDP/TLS/RTP/SAVPF 107 100 101 116 117 96' * @private */ function setMLineDefaultCodec(mLine, codecId) { var elements = mLine.split(' '); // Copy first three elements, codec order starts on fourth. var newLine = elements.slice(0, 3); // Put target |codecId| first and copy the rest. newLine.push(codecId); for (var i = 3; i < elements.length; i++) { if (elements[i] != codecId) newLine.push(elements[i]); } return newLine.join(' '); } /** * Returns the default codec's ID from the |mLine|, or null if the codec list is * empty. The default codec is the codec whose ID is first in the list of codec * IDs on the |mLine|. For example, getMLineDefaultCodec( * 'm=video 9 UDP/TLS/RTP/SAVPF 100 101 107 116 117 96') * Returns: * 100 * @private */ function getMLineDefaultCodec(mLine) { var elements = mLine.split(' '); if (elements.length < 4) return null; return parseInt(elements[3]); } /** @private */ function splitSdpLines(sdp) { return sdp.split('\r\n'); } /** @private */ function mergeSdpLines(sdpLines) { return sdpLines.join('\r\n'); } /** @private */ function findLine(lines, startsWith) { for (var i = 0; i < lines.length; i++) { if (lines[i].startsWith(startsWith)) return i; } return null; }