Home | History | Annotate | Download | only in webrtc
      1 /*
      2  * libjingle
      3  * Copyright 2014 Google Inc.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are met:
      7  *
      8  *  1. Redistributions of source code must retain the above copyright notice,
      9  *     this list of conditions and the following disclaimer.
     10  *  2. Redistributions in binary form must reproduce the above copyright notice,
     11  *     this list of conditions and the following disclaimer in the documentation
     12  *     and/or other materials provided with the distribution.
     13  *  3. The name of the author may not be used to endorse or promote products
     14  *     derived from this software without specific prior written permission.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
     17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
     19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
     25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26  */
     27 
     28 #include <stdio.h>
     29 
     30 #include "talk/media/base/streamparams.h"
     31 #include "talk/media/webrtc/simulcast.h"
     32 #include "webrtc/base/arraysize.h"
     33 #include "webrtc/base/common.h"
     34 #include "webrtc/base/logging.h"
     35 #include "webrtc/system_wrappers/include/field_trial.h"
     36 
     37 namespace cricket {
     38 
     39 struct SimulcastFormat {
     40   int width;
     41   int height;
     42   // The maximum number of simulcast layers can be used for
     43   // resolutions at |widthxheigh|.
     44   size_t max_layers;
     45   // The maximum bitrate for encoding stream at |widthxheight|, when we are
     46   // not sending the next higher spatial stream.
     47   int max_bitrate_kbps;
     48   // The target bitrate for encoding stream at |widthxheight|, when this layer
     49   // is not the highest layer (i.e., when we are sending another higher spatial
     50   // stream).
     51   int target_bitrate_kbps;
     52   // The minimum bitrate needed for encoding stream at |widthxheight|.
     53   int min_bitrate_kbps;
     54 };
     55 
     56 // These tables describe from which resolution we can use how many
     57 // simulcast layers at what bitrates (maximum, target, and minimum).
     58 // Important!! Keep this table from high resolution to low resolution.
     59 const SimulcastFormat kSimulcastFormats[] = {
     60   {1920, 1080, 3, 5000, 4000, 800},
     61   {1280, 720, 3,  2500, 2500, 600},
     62   {960, 540, 3, 900, 900, 450},
     63   {640, 360, 2, 700, 500, 150},
     64   {480, 270, 2, 450, 350, 150},
     65   {320, 180, 1, 200, 150, 30},
     66   {0, 0, 1, 200, 150, 30}
     67 };
     68 
     69 // Multiway: Number of temporal layers for each simulcast stream, for maximum
     70 // possible number of simulcast streams |kMaxSimulcastStreams|. The array
     71 // goes from lowest resolution at position 0 to highest resolution.
     72 // For example, first three elements correspond to say: QVGA, VGA, WHD.
     73 static const int
     74     kDefaultConferenceNumberOfTemporalLayers[webrtc::kMaxSimulcastStreams] =
     75     {3, 3, 3, 3};
     76 
     77 void GetSimulcastSsrcs(const StreamParams& sp, std::vector<uint32_t>* ssrcs) {
     78   const SsrcGroup* sim_group = sp.get_ssrc_group(kSimSsrcGroupSemantics);
     79   if (sim_group) {
     80     ssrcs->insert(
     81         ssrcs->end(), sim_group->ssrcs.begin(), sim_group->ssrcs.end());
     82   }
     83 }
     84 
     85 void MaybeExchangeWidthHeight(int* width, int* height) {
     86   // |kSimulcastFormats| assumes |width| >= |height|. If not, exchange them
     87   // before comparing.
     88   if (*width < *height) {
     89     int temp = *width;
     90     *width = *height;
     91     *height = temp;
     92   }
     93 }
     94 
     95 int FindSimulcastFormatIndex(int width, int height) {
     96   MaybeExchangeWidthHeight(&width, &height);
     97 
     98   for (int i = 0; i < arraysize(kSimulcastFormats); ++i) {
     99     if (width >= kSimulcastFormats[i].width &&
    100         height >= kSimulcastFormats[i].height) {
    101       return i;
    102     }
    103   }
    104   return -1;
    105 }
    106 
    107 int FindSimulcastFormatIndex(int width, int height, size_t max_layers) {
    108   MaybeExchangeWidthHeight(&width, &height);
    109 
    110   for (int i = 0; i < arraysize(kSimulcastFormats); ++i) {
    111     if (width >= kSimulcastFormats[i].width &&
    112         height >= kSimulcastFormats[i].height &&
    113         max_layers == kSimulcastFormats[i].max_layers) {
    114       return i;
    115     }
    116   }
    117   return -1;
    118 }
    119 
    120 // Simulcast stream width and height must both be dividable by
    121 // |2 ^ simulcast_layers - 1|.
    122 int NormalizeSimulcastSize(int size, size_t simulcast_layers) {
    123   const int base2_exponent = static_cast<int>(simulcast_layers) - 1;
    124   return ((size >> base2_exponent) << base2_exponent);
    125 }
    126 
    127 size_t FindSimulcastMaxLayers(int width, int height) {
    128   int index = FindSimulcastFormatIndex(width, height);
    129   if (index == -1) {
    130     return -1;
    131   }
    132   return kSimulcastFormats[index].max_layers;
    133 }
    134 
    135 // TODO(marpan): Investigate if we should return 0 instead of -1 in
    136 // FindSimulcast[Max/Target/Min]Bitrate functions below, since the
    137 // codec struct max/min/targeBitrates are unsigned.
    138 int FindSimulcastMaxBitrateBps(int width, int height, size_t max_layers) {
    139   const int format_index = FindSimulcastFormatIndex(width, height);
    140   if (format_index == -1) {
    141     return -1;
    142   }
    143   return kSimulcastFormats[format_index].max_bitrate_kbps * 1000;
    144 }
    145 
    146 int FindSimulcastTargetBitrateBps(int width,
    147                                   int height,
    148                                   size_t max_layers) {
    149   const int format_index = FindSimulcastFormatIndex(width, height);
    150   if (format_index == -1) {
    151     return -1;
    152   }
    153   return kSimulcastFormats[format_index].target_bitrate_kbps * 1000;
    154 }
    155 
    156 int FindSimulcastMinBitrateBps(int width, int height, size_t max_layers) {
    157   const int format_index = FindSimulcastFormatIndex(width, height);
    158   if (format_index == -1) {
    159     return -1;
    160   }
    161   return kSimulcastFormats[format_index].min_bitrate_kbps * 1000;
    162 }
    163 
    164 bool SlotSimulcastMaxResolution(size_t max_layers, int* width, int* height) {
    165   int index = FindSimulcastFormatIndex(*width, *height, max_layers);
    166   if (index == -1) {
    167     LOG(LS_ERROR) << "SlotSimulcastMaxResolution";
    168     return false;
    169   }
    170 
    171   *width = kSimulcastFormats[index].width;
    172   *height = kSimulcastFormats[index].height;
    173   LOG(LS_INFO) << "SlotSimulcastMaxResolution to width:" << *width
    174                << " height:" << *height;
    175   return true;
    176 }
    177 
    178 int GetTotalMaxBitrateBps(const std::vector<webrtc::VideoStream>& streams) {
    179   int total_max_bitrate_bps = 0;
    180   for (size_t s = 0; s < streams.size() - 1; ++s) {
    181     total_max_bitrate_bps += streams[s].target_bitrate_bps;
    182   }
    183   total_max_bitrate_bps += streams.back().max_bitrate_bps;
    184   return total_max_bitrate_bps;
    185 }
    186 
    187 std::vector<webrtc::VideoStream> GetSimulcastConfig(
    188     size_t max_streams,
    189     int width,
    190     int height,
    191     int max_bitrate_bps,
    192     int max_qp,
    193     int max_framerate) {
    194   size_t simulcast_layers = FindSimulcastMaxLayers(width, height);
    195   if (simulcast_layers > max_streams) {
    196     // If the number of SSRCs in the group differs from our target
    197     // number of simulcast streams for current resolution, switch down
    198     // to a resolution that matches our number of SSRCs.
    199     if (!SlotSimulcastMaxResolution(max_streams, &width, &height)) {
    200       return std::vector<webrtc::VideoStream>();
    201     }
    202     simulcast_layers = max_streams;
    203   }
    204   std::vector<webrtc::VideoStream> streams;
    205   streams.resize(simulcast_layers);
    206 
    207   // Format width and height has to be divisible by |2 ^ number_streams - 1|.
    208   width = NormalizeSimulcastSize(width, simulcast_layers);
    209   height = NormalizeSimulcastSize(height, simulcast_layers);
    210 
    211   // Add simulcast sub-streams from lower resolution to higher resolutions.
    212   // Add simulcast streams, from highest resolution (|s| = number_streams -1)
    213   // to lowest resolution at |s| = 0.
    214   for (size_t s = simulcast_layers - 1;; --s) {
    215     streams[s].width = width;
    216     streams[s].height = height;
    217     // TODO(pbos): Fill actual temporal-layer bitrate thresholds.
    218     streams[s].temporal_layer_thresholds_bps.resize(
    219         kDefaultConferenceNumberOfTemporalLayers[s] - 1);
    220     streams[s].max_bitrate_bps =
    221         FindSimulcastMaxBitrateBps(width, height, simulcast_layers);
    222     streams[s].target_bitrate_bps =
    223         FindSimulcastTargetBitrateBps(width, height, simulcast_layers);
    224     streams[s].min_bitrate_bps =
    225         FindSimulcastMinBitrateBps(width, height, simulcast_layers);
    226     streams[s].max_qp = max_qp;
    227     streams[s].max_framerate = max_framerate;
    228     width /= 2;
    229     height /= 2;
    230     if (s == 0) {
    231       break;
    232     }
    233   }
    234 
    235   // Spend additional bits to boost the max stream.
    236   int bitrate_left_bps = max_bitrate_bps - GetTotalMaxBitrateBps(streams);
    237   if (bitrate_left_bps > 0) {
    238     streams.back().max_bitrate_bps += bitrate_left_bps;
    239   }
    240 
    241   return streams;
    242 }
    243 
    244 static const int kScreenshareMinBitrateKbps = 50;
    245 static const int kScreenshareMaxBitrateKbps = 6000;
    246 static const int kScreenshareDefaultTl0BitrateKbps = 200;
    247 static const int kScreenshareDefaultTl1BitrateKbps = 1000;
    248 
    249 static const char* kScreencastLayerFieldTrialName =
    250     "WebRTC-ScreenshareLayerRates";
    251 
    252 ScreenshareLayerConfig::ScreenshareLayerConfig(int tl0_bitrate, int tl1_bitrate)
    253     : tl0_bitrate_kbps(tl0_bitrate), tl1_bitrate_kbps(tl1_bitrate) {
    254 }
    255 
    256 ScreenshareLayerConfig ScreenshareLayerConfig::GetDefault() {
    257   std::string group =
    258       webrtc::field_trial::FindFullName(kScreencastLayerFieldTrialName);
    259 
    260   ScreenshareLayerConfig config(kScreenshareDefaultTl0BitrateKbps,
    261                                 kScreenshareDefaultTl1BitrateKbps);
    262   if (!group.empty() && !FromFieldTrialGroup(group, &config)) {
    263     LOG(LS_WARNING) << "Unable to parse WebRTC-ScreenshareLayerRates"
    264                        " field trial group: '" << group << "'.";
    265   }
    266   return config;
    267 }
    268 
    269 bool ScreenshareLayerConfig::FromFieldTrialGroup(
    270     const std::string& group,
    271     ScreenshareLayerConfig* config) {
    272   // Parse field trial group name, containing bitrates for tl0 and tl1.
    273   int tl0_bitrate;
    274   int tl1_bitrate;
    275   if (sscanf(group.c_str(), "%d-%d", &tl0_bitrate, &tl1_bitrate) != 2) {
    276     return false;
    277   }
    278 
    279   // Sanity check.
    280   if (tl0_bitrate < kScreenshareMinBitrateKbps ||
    281       tl0_bitrate > kScreenshareMaxBitrateKbps ||
    282       tl1_bitrate < kScreenshareMinBitrateKbps ||
    283       tl1_bitrate > kScreenshareMaxBitrateKbps || tl0_bitrate > tl1_bitrate) {
    284     return false;
    285   }
    286 
    287   config->tl0_bitrate_kbps = tl0_bitrate;
    288   config->tl1_bitrate_kbps = tl1_bitrate;
    289 
    290   return true;
    291 }
    292 
    293 }  // namespace cricket
    294