blob: f8415c3e4efde49c9ae3c5719fa9d76a176b3dad [file] [log] [blame]
/**
* 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 <id> from 'a=rtpmap:<id> <codec>/<rate>'.
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 <codec> from 'a=rtpmap:<id> <codec>/<rate>'.
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:<id> <codec>/<rate>'.
*/
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:<id> <codec>/<rate>'.
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;
}