1 /*
2 * Copyright (c) 2017 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 <stdio.h>
12
13 #ifdef WIN32
14 #include <winsock2.h>
15 #endif
16 #if defined(WEBRTC_LINUX) || defined(WEBRTC_FUCHSIA)
17 #include <netinet/in.h>
18 #endif
19
20 #include <iostream>
21 #include <map>
22 #include <string>
23 #include <vector>
24
25 #include "absl/flags/flag.h"
26 #include "absl/flags/parse.h"
27 #include "absl/memory/memory.h"
28 #include "api/audio/audio_frame.h"
29 #include "api/audio_codecs/L16/audio_encoder_L16.h"
30 #include "api/audio_codecs/g711/audio_encoder_g711.h"
31 #include "api/audio_codecs/g722/audio_encoder_g722.h"
32 #include "api/audio_codecs/ilbc/audio_encoder_ilbc.h"
33 #include "api/audio_codecs/opus/audio_encoder_opus.h"
34 #include "modules/audio_coding/codecs/cng/audio_encoder_cng.h"
35 #include "modules/audio_coding/include/audio_coding_module.h"
36 #include "modules/audio_coding/neteq/tools/input_audio_file.h"
37 #include "rtc_base/numerics/safe_conversions.h"
38
39 ABSL_FLAG(bool, list_codecs, false, "Enumerate all codecs");
40 ABSL_FLAG(std::string, codec, "opus", "Codec to use");
41 ABSL_FLAG(int,
42 frame_len,
43 0,
44 "Frame length in ms; 0 indicates codec default value");
45 ABSL_FLAG(int, bitrate, 0, "Bitrate in kbps; 0 indicates codec default value");
46 ABSL_FLAG(int,
47 payload_type,
48 -1,
49 "RTP payload type; -1 indicates codec default value");
50 ABSL_FLAG(int,
51 cng_payload_type,
52 -1,
53 "RTP payload type for CNG; -1 indicates default value");
54 ABSL_FLAG(int, ssrc, 0, "SSRC to write to the RTP header");
55 ABSL_FLAG(bool, dtx, false, "Use DTX/CNG");
56 ABSL_FLAG(int, sample_rate, 48000, "Sample rate of the input file");
57
58 namespace webrtc {
59 namespace test {
60 namespace {
61
62 // Add new codecs here, and to the map below.
63 enum class CodecType {
64 kOpus,
65 kPcmU,
66 kPcmA,
67 kG722,
68 kPcm16b8,
69 kPcm16b16,
70 kPcm16b32,
71 kPcm16b48,
72 kIlbc,
73 };
74
75 struct CodecTypeAndInfo {
76 CodecType type;
77 int default_payload_type;
78 bool internal_dtx;
79 };
80
81 // List all supported codecs here. This map defines the command-line parameter
82 // value (the key string) for selecting each codec, together with information
83 // whether it is using internal or external DTX/CNG.
CodecList()84 const std::map<std::string, CodecTypeAndInfo>& CodecList() {
85 static const auto* const codec_list =
86 new std::map<std::string, CodecTypeAndInfo>{
87 {"opus", {CodecType::kOpus, 111, true}},
88 {"pcmu", {CodecType::kPcmU, 0, false}},
89 {"pcma", {CodecType::kPcmA, 8, false}},
90 {"g722", {CodecType::kG722, 9, false}},
91 {"pcm16b_8", {CodecType::kPcm16b8, 93, false}},
92 {"pcm16b_16", {CodecType::kPcm16b16, 94, false}},
93 {"pcm16b_32", {CodecType::kPcm16b32, 95, false}},
94 {"pcm16b_48", {CodecType::kPcm16b48, 96, false}},
95 {"ilbc", {CodecType::kIlbc, 102, false}}};
96 return *codec_list;
97 }
98
99 // This class will receive callbacks from ACM when a packet is ready, and write
100 // it to the output file.
101 class Packetizer : public AudioPacketizationCallback {
102 public:
Packetizer(FILE * out_file,uint32_t ssrc,int timestamp_rate_hz)103 Packetizer(FILE* out_file, uint32_t ssrc, int timestamp_rate_hz)
104 : out_file_(out_file),
105 ssrc_(ssrc),
106 timestamp_rate_hz_(timestamp_rate_hz) {}
107
SendData(AudioFrameType frame_type,uint8_t payload_type,uint32_t timestamp,const uint8_t * payload_data,size_t payload_len_bytes,int64_t absolute_capture_timestamp_ms)108 int32_t SendData(AudioFrameType frame_type,
109 uint8_t payload_type,
110 uint32_t timestamp,
111 const uint8_t* payload_data,
112 size_t payload_len_bytes,
113 int64_t absolute_capture_timestamp_ms) override {
114 if (payload_len_bytes == 0) {
115 return 0;
116 }
117
118 constexpr size_t kRtpHeaderLength = 12;
119 constexpr size_t kRtpDumpHeaderLength = 8;
120 const uint16_t length = htons(rtc::checked_cast<uint16_t>(
121 kRtpHeaderLength + kRtpDumpHeaderLength + payload_len_bytes));
122 const uint16_t plen = htons(
123 rtc::checked_cast<uint16_t>(kRtpHeaderLength + payload_len_bytes));
124 const uint32_t offset = htonl(timestamp / (timestamp_rate_hz_ / 1000));
125 RTC_CHECK_EQ(fwrite(&length, sizeof(uint16_t), 1, out_file_), 1);
126 RTC_CHECK_EQ(fwrite(&plen, sizeof(uint16_t), 1, out_file_), 1);
127 RTC_CHECK_EQ(fwrite(&offset, sizeof(uint32_t), 1, out_file_), 1);
128
129 const uint8_t rtp_header[] = {0x80,
130 static_cast<uint8_t>(payload_type & 0x7F),
131 static_cast<uint8_t>(sequence_number_ >> 8),
132 static_cast<uint8_t>(sequence_number_),
133 static_cast<uint8_t>(timestamp >> 24),
134 static_cast<uint8_t>(timestamp >> 16),
135 static_cast<uint8_t>(timestamp >> 8),
136 static_cast<uint8_t>(timestamp),
137 static_cast<uint8_t>(ssrc_ >> 24),
138 static_cast<uint8_t>(ssrc_ >> 16),
139 static_cast<uint8_t>(ssrc_ >> 8),
140 static_cast<uint8_t>(ssrc_)};
141 static_assert(sizeof(rtp_header) == kRtpHeaderLength, "");
142 RTC_CHECK_EQ(
143 fwrite(rtp_header, sizeof(uint8_t), kRtpHeaderLength, out_file_),
144 kRtpHeaderLength);
145 ++sequence_number_; // Intended to wrap on overflow.
146
147 RTC_CHECK_EQ(
148 fwrite(payload_data, sizeof(uint8_t), payload_len_bytes, out_file_),
149 payload_len_bytes);
150
151 return 0;
152 }
153
154 private:
155 FILE* const out_file_;
156 const uint32_t ssrc_;
157 const int timestamp_rate_hz_;
158 uint16_t sequence_number_ = 0;
159 };
160
SetFrameLenIfFlagIsPositive(int * config_frame_len)161 void SetFrameLenIfFlagIsPositive(int* config_frame_len) {
162 if (absl::GetFlag(FLAGS_frame_len) > 0) {
163 *config_frame_len = absl::GetFlag(FLAGS_frame_len);
164 }
165 }
166
167 template <typename T>
GetCodecConfig()168 typename T::Config GetCodecConfig() {
169 typename T::Config config;
170 SetFrameLenIfFlagIsPositive(&config.frame_size_ms);
171 RTC_CHECK(config.IsOk());
172 return config;
173 }
174
Pcm16bConfig(CodecType codec_type)175 AudioEncoderL16::Config Pcm16bConfig(CodecType codec_type) {
176 auto config = GetCodecConfig<AudioEncoderL16>();
177 switch (codec_type) {
178 case CodecType::kPcm16b8:
179 config.sample_rate_hz = 8000;
180 return config;
181 case CodecType::kPcm16b16:
182 config.sample_rate_hz = 16000;
183 return config;
184 case CodecType::kPcm16b32:
185 config.sample_rate_hz = 32000;
186 return config;
187 case CodecType::kPcm16b48:
188 config.sample_rate_hz = 48000;
189 return config;
190 default:
191 RTC_DCHECK_NOTREACHED();
192 return config;
193 }
194 }
195
CreateEncoder(CodecType codec_type,int payload_type)196 std::unique_ptr<AudioEncoder> CreateEncoder(CodecType codec_type,
197 int payload_type) {
198 switch (codec_type) {
199 case CodecType::kOpus: {
200 AudioEncoderOpus::Config config = GetCodecConfig<AudioEncoderOpus>();
201 if (absl::GetFlag(FLAGS_bitrate) > 0) {
202 config.bitrate_bps = absl::GetFlag(FLAGS_bitrate);
203 }
204 config.dtx_enabled = absl::GetFlag(FLAGS_dtx);
205 RTC_CHECK(config.IsOk());
206 return AudioEncoderOpus::MakeAudioEncoder(config, payload_type);
207 }
208
209 case CodecType::kPcmU:
210 case CodecType::kPcmA: {
211 AudioEncoderG711::Config config = GetCodecConfig<AudioEncoderG711>();
212 config.type = codec_type == CodecType::kPcmU
213 ? AudioEncoderG711::Config::Type::kPcmU
214 : AudioEncoderG711::Config::Type::kPcmA;
215 RTC_CHECK(config.IsOk());
216 return AudioEncoderG711::MakeAudioEncoder(config, payload_type);
217 }
218
219 case CodecType::kG722: {
220 return AudioEncoderG722::MakeAudioEncoder(
221 GetCodecConfig<AudioEncoderG722>(), payload_type);
222 }
223
224 case CodecType::kPcm16b8:
225 case CodecType::kPcm16b16:
226 case CodecType::kPcm16b32:
227 case CodecType::kPcm16b48: {
228 return AudioEncoderL16::MakeAudioEncoder(Pcm16bConfig(codec_type),
229 payload_type);
230 }
231
232 case CodecType::kIlbc: {
233 return AudioEncoderIlbc::MakeAudioEncoder(
234 GetCodecConfig<AudioEncoderIlbc>(), payload_type);
235 }
236 }
237 RTC_DCHECK_NOTREACHED();
238 return nullptr;
239 }
240
GetCngConfig(int sample_rate_hz)241 AudioEncoderCngConfig GetCngConfig(int sample_rate_hz) {
242 AudioEncoderCngConfig cng_config;
243 const auto default_payload_type = [&] {
244 switch (sample_rate_hz) {
245 case 8000:
246 return 13;
247 case 16000:
248 return 98;
249 case 32000:
250 return 99;
251 case 48000:
252 return 100;
253 default:
254 RTC_DCHECK_NOTREACHED();
255 }
256 return 0;
257 };
258 cng_config.payload_type = absl::GetFlag(FLAGS_cng_payload_type) != -1
259 ? absl::GetFlag(FLAGS_cng_payload_type)
260 : default_payload_type();
261 return cng_config;
262 }
263
RunRtpEncode(int argc,char * argv[])264 int RunRtpEncode(int argc, char* argv[]) {
265 std::vector<char*> args = absl::ParseCommandLine(argc, argv);
266 const std::string usage =
267 "Tool for generating an RTP dump file from audio input.\n"
268 "Example usage:\n"
269 "./rtp_encode input.pcm output.rtp --codec=[codec] "
270 "--frame_len=[frame_len] --bitrate=[bitrate]\n\n";
271 if (!absl::GetFlag(FLAGS_list_codecs) && args.size() != 3) {
272 printf("%s", usage.c_str());
273 return 1;
274 }
275
276 if (absl::GetFlag(FLAGS_list_codecs)) {
277 printf("The following arguments are valid --codec parameters:\n");
278 for (const auto& c : CodecList()) {
279 printf(" %s\n", c.first.c_str());
280 }
281 return 0;
282 }
283
284 const auto codec_it = CodecList().find(absl::GetFlag(FLAGS_codec));
285 if (codec_it == CodecList().end()) {
286 printf("%s is not a valid codec name.\n",
287 absl::GetFlag(FLAGS_codec).c_str());
288 printf("Use argument --list_codecs to see all valid codec names.\n");
289 return 1;
290 }
291
292 // Create the codec.
293 const int payload_type = absl::GetFlag(FLAGS_payload_type) == -1
294 ? codec_it->second.default_payload_type
295 : absl::GetFlag(FLAGS_payload_type);
296 std::unique_ptr<AudioEncoder> codec =
297 CreateEncoder(codec_it->second.type, payload_type);
298
299 // Create an external VAD/CNG encoder if needed.
300 if (absl::GetFlag(FLAGS_dtx) && !codec_it->second.internal_dtx) {
301 AudioEncoderCngConfig cng_config = GetCngConfig(codec->SampleRateHz());
302 RTC_DCHECK(codec);
303 cng_config.speech_encoder = std::move(codec);
304 codec = CreateComfortNoiseEncoder(std::move(cng_config));
305 }
306 RTC_DCHECK(codec);
307
308 // Set up ACM.
309 const int timestamp_rate_hz = codec->RtpTimestampRateHz();
310 AudioCodingModule::Config config;
311 std::unique_ptr<AudioCodingModule> acm(AudioCodingModule::Create(config));
312 acm->SetEncoder(std::move(codec));
313
314 // Open files.
315 printf("Input file: %s\n", args[1]);
316 InputAudioFile input_file(args[1], false); // Open input in non-looping mode.
317 FILE* out_file = fopen(args[2], "wb");
318 RTC_CHECK(out_file) << "Could not open file " << args[2] << " for writing";
319 printf("Output file: %s\n", args[2]);
320 fprintf(out_file, "#!rtpplay1.0 \n"); //,
321 // Write 3 32-bit values followed by 2 16-bit values, all set to 0. This means
322 // a total of 16 bytes.
323 const uint8_t file_header[16] = {0};
324 RTC_CHECK_EQ(fwrite(file_header, sizeof(file_header), 1, out_file), 1);
325
326 // Create and register the packetizer, which will write the packets to file.
327 Packetizer packetizer(out_file, absl::GetFlag(FLAGS_ssrc), timestamp_rate_hz);
328 RTC_DCHECK_EQ(acm->RegisterTransportCallback(&packetizer), 0);
329
330 AudioFrame audio_frame;
331 audio_frame.samples_per_channel_ =
332 absl::GetFlag(FLAGS_sample_rate) / 100; // 10 ms
333 audio_frame.sample_rate_hz_ = absl::GetFlag(FLAGS_sample_rate);
334 audio_frame.num_channels_ = 1;
335
336 while (input_file.Read(audio_frame.samples_per_channel_,
337 audio_frame.mutable_data())) {
338 RTC_CHECK_GE(acm->Add10MsData(audio_frame), 0);
339 audio_frame.timestamp_ += audio_frame.samples_per_channel_;
340 }
341
342 return 0;
343 }
344
345 } // namespace
346 } // namespace test
347 } // namespace webrtc
348
main(int argc,char * argv[])349 int main(int argc, char* argv[]) {
350 return webrtc::test::RunRtpEncode(argc, argv);
351 }
352