1 /*
2  *  Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 #include "api/audio_codecs/opus/audio_encoder_multi_channel_opus.h"
12 
13 #include "test/gmock.h"
14 
15 namespace webrtc {
16 using ::testing::NiceMock;
17 using ::testing::Return;
18 
19 namespace {
20 constexpr int kOpusPayloadType = 120;
21 }  // namespace
22 
TEST(AudioEncoderMultiOpusTest,CheckConfigValidity)23 TEST(AudioEncoderMultiOpusTest, CheckConfigValidity) {
24   {
25     const SdpAudioFormat sdp_format("multiopus", 48000, 2,
26                                     {{"channel_mapping", "3,0"},
27                                      {"coupled_streams", "1"},
28                                      {"num_streams", "2"}});
29     const absl::optional<AudioEncoderMultiChannelOpus::Config> encoder_config =
30         AudioEncoderMultiChannelOpus::SdpToConfig(sdp_format);
31 
32     // Maps input channel 0 to coded channel 3, which doesn't exist.
33     EXPECT_FALSE(encoder_config.has_value());
34   }
35 
36   {
37     const SdpAudioFormat sdp_format("multiopus", 48000, 2,
38                                     {{"channel_mapping", "0"},
39                                      {"coupled_streams", "1"},
40                                      {"num_streams", "2"}});
41     const absl::optional<AudioEncoderMultiChannelOpus::Config> encoder_config =
42         AudioEncoderMultiChannelOpus::SdpToConfig(sdp_format);
43 
44     // The mapping is too short.
45     EXPECT_FALSE(encoder_config.has_value());
46   }
47   {
48     const SdpAudioFormat sdp_format("multiopus", 48000, 3,
49                                     {{"channel_mapping", "0,0,0"},
50                                      {"coupled_streams", "0"},
51                                      {"num_streams", "1"}});
52     const absl::optional<AudioEncoderMultiChannelOpus::Config> encoder_config =
53         AudioEncoderMultiChannelOpus::SdpToConfig(sdp_format);
54 
55     // Coded channel 0 comes from both input channels 0, 1 and 2.
56     EXPECT_FALSE(encoder_config.has_value());
57   }
58   {
59     const SdpAudioFormat sdp_format("multiopus", 48000, 3,
60                                     {{"channel_mapping", "0,255,255"},
61                                      {"coupled_streams", "0"},
62                                      {"num_streams", "1"}});
63     const absl::optional<AudioEncoderMultiChannelOpus::Config> encoder_config =
64         AudioEncoderMultiChannelOpus::SdpToConfig(sdp_format);
65     ASSERT_TRUE(encoder_config.has_value());
66 
67     // This is fine, because channels 1, 2 are set to be ignored.
68     EXPECT_TRUE(encoder_config->IsOk());
69   }
70   {
71     const SdpAudioFormat sdp_format("multiopus", 48000, 3,
72                                     {{"channel_mapping", "0,255,255"},
73                                      {"coupled_streams", "0"},
74                                      {"num_streams", "2"}});
75     const absl::optional<AudioEncoderMultiChannelOpus::Config> encoder_config =
76         AudioEncoderMultiChannelOpus::SdpToConfig(sdp_format);
77 
78     // This is NOT fine, because channels nothing says how coded channel 1
79     // should be coded.
80     EXPECT_FALSE(encoder_config.has_value());
81   }
82 }
83 
TEST(AudioEncoderMultiOpusTest,ConfigValuesAreParsedCorrectly)84 TEST(AudioEncoderMultiOpusTest, ConfigValuesAreParsedCorrectly) {
85   SdpAudioFormat sdp_format({"multiopus",
86                              48000,
87                              6,
88                              {{"minptime", "10"},
89                               {"useinbandfec", "1"},
90                               {"channel_mapping", "0,4,1,2,3,5"},
91                               {"num_streams", "4"},
92                               {"coupled_streams", "2"}}});
93   const absl::optional<AudioEncoderMultiChannelOpus::Config> encoder_config =
94       AudioEncoderMultiChannelOpus::SdpToConfig(sdp_format);
95   ASSERT_TRUE(encoder_config.has_value());
96 
97   EXPECT_EQ(encoder_config->coupled_streams, 2);
98   EXPECT_EQ(encoder_config->num_streams, 4);
99   EXPECT_THAT(
100       encoder_config->channel_mapping,
101       testing::ContainerEq(std::vector<unsigned char>({0, 4, 1, 2, 3, 5})));
102 }
103 
TEST(AudioEncoderMultiOpusTest,CreateFromValidConfig)104 TEST(AudioEncoderMultiOpusTest, CreateFromValidConfig) {
105   {
106     const SdpAudioFormat sdp_format("multiopus", 48000, 3,
107                                     {{"channel_mapping", "0,255,255"},
108                                      {"coupled_streams", "0"},
109                                      {"num_streams", "2"}});
110     const absl::optional<AudioEncoderMultiChannelOpus::Config> encoder_config =
111         AudioEncoderMultiChannelOpus::SdpToConfig(sdp_format);
112     ASSERT_FALSE(encoder_config.has_value());
113   }
114   {
115     const SdpAudioFormat sdp_format("multiopus", 48000, 3,
116                                     {{"channel_mapping", "1,255,0"},
117                                      {"coupled_streams", "1"},
118                                      {"num_streams", "1"}});
119     const absl::optional<AudioEncoderMultiChannelOpus::Config> encoder_config =
120         AudioEncoderMultiChannelOpus::SdpToConfig(sdp_format);
121     ASSERT_TRUE(encoder_config.has_value());
122 
123     EXPECT_THAT(encoder_config->channel_mapping,
124                 testing::ContainerEq(std::vector<unsigned char>({1, 255, 0})));
125 
126     EXPECT_TRUE(encoder_config->IsOk());
127 
128     const std::unique_ptr<AudioEncoder> opus_encoder =
129         AudioEncoderMultiChannelOpus::MakeAudioEncoder(*encoder_config,
130                                                        kOpusPayloadType);
131 
132     // Creating an encoder from a valid config should work.
133     EXPECT_TRUE(opus_encoder);
134   }
135 }
136 
TEST(AudioEncoderMultiOpusTest,AdvertisedCodecsCanBeCreated)137 TEST(AudioEncoderMultiOpusTest, AdvertisedCodecsCanBeCreated) {
138   std::vector<AudioCodecSpec> specs;
139   AudioEncoderMultiChannelOpus::AppendSupportedEncoders(&specs);
140 
141   EXPECT_FALSE(specs.empty());
142 
143   for (const AudioCodecSpec& spec : specs) {
144     const absl::optional<AudioEncoderMultiChannelOpus::Config> encoder_config =
145         AudioEncoderMultiChannelOpus::SdpToConfig(spec.format);
146     ASSERT_TRUE(encoder_config.has_value());
147 
148     const std::unique_ptr<AudioEncoder> opus_encoder =
149         AudioEncoderMultiChannelOpus::MakeAudioEncoder(*encoder_config,
150                                                        kOpusPayloadType);
151 
152     EXPECT_TRUE(opus_encoder);
153   }
154 }
155 
156 }  // namespace webrtc
157