Home | History | Annotate | Download | only in video_WebRtcPeerConnectionWithCamera
      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  */
     13 function 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  */
     23 function 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  *   */
     48 function 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  */
     58 function 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  */
     92 function 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  */
    108 function 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  */
    126 function 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  */
    150 function 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  */
    175 function getMLineDefaultCodec(mLine) {
    176   var elements = mLine.split(' ');
    177   if (elements.length < 4)
    178     return null;
    179   return parseInt(elements[3]);
    180 }
    181 
    182 /** @private */
    183 function splitSdpLines(sdp) {
    184   return sdp.split('\r\n');
    185 }
    186 
    187 /** @private */
    188 function mergeSdpLines(sdpLines) {
    189   return sdpLines.join('\r\n');
    190 }
    191 
    192 /** @private */
    193 function 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