blob: f65679de4f4b2b1c446cbd6bf01e487db4e8f16f [file] [log] [blame]
Henrik Lundin1391bd42017-11-24 09:28:57 +01001/*
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#ifdef WEBRTC_LINUX
17#include <netinet/in.h>
18#endif
19
20#include <iostream>
21#include <map>
22#include <string>
Mirko Bonadei14be7992019-06-27 15:59:09 +020023#include <vector>
Henrik Lundin1391bd42017-11-24 09:28:57 +010024
Mirko Bonadei14be7992019-06-27 15:59:09 +020025#include "absl/flags/flag.h"
26#include "absl/flags/parse.h"
Karl Wiberg918f50c2018-07-05 11:40:33 +020027#include "absl/memory/memory.h"
Fredrik Solenbergbbf21a32018-04-12 22:44:09 +020028#include "api/audio/audio_frame.h"
Henrik Lundin1391bd42017-11-24 09:28:57 +010029#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/isac/audio_encoder_isac.h"
34#include "api/audio_codecs/opus/audio_encoder_opus.h"
35#include "modules/audio_coding/codecs/cng/audio_encoder_cng.h"
36#include "modules/audio_coding/include/audio_coding_module.h"
37#include "modules/audio_coding/neteq/tools/input_audio_file.h"
Henrik Lundin1391bd42017-11-24 09:28:57 +010038#include "rtc_base/numerics/safe_conversions.h"
Henrik Lundin1391bd42017-11-24 09:28:57 +010039
Mirko Bonadei14be7992019-06-27 15:59:09 +020040ABSL_FLAG(bool, list_codecs, false, "Enumerate all codecs");
41ABSL_FLAG(std::string, codec, "opus", "Codec to use");
42ABSL_FLAG(int,
43 frame_len,
44 0,
45 "Frame length in ms; 0 indicates codec default value");
46ABSL_FLAG(int, bitrate, 0, "Bitrate in kbps; 0 indicates codec default value");
47ABSL_FLAG(int,
48 payload_type,
49 -1,
50 "RTP payload type; -1 indicates codec default value");
51ABSL_FLAG(int,
52 cng_payload_type,
53 -1,
54 "RTP payload type for CNG; -1 indicates default value");
55ABSL_FLAG(int, ssrc, 0, "SSRC to write to the RTP header");
56ABSL_FLAG(bool, dtx, false, "Use DTX/CNG");
57ABSL_FLAG(int, sample_rate, 48000, "Sample rate of the input file");
58
Henrik Lundin1391bd42017-11-24 09:28:57 +010059namespace webrtc {
60namespace test {
61namespace {
62
Henrik Lundin1391bd42017-11-24 09:28:57 +010063// Add new codecs here, and to the map below.
64enum class CodecType {
65 kOpus,
66 kPcmU,
67 kPcmA,
68 kG722,
69 kPcm16b8,
70 kPcm16b16,
71 kPcm16b32,
72 kPcm16b48,
73 kIlbc,
74 kIsac
75};
76
77struct CodecTypeAndInfo {
78 CodecType type;
79 int default_payload_type;
80 bool internal_dtx;
81};
82
83// List all supported codecs here. This map defines the command-line parameter
84// value (the key string) for selecting each codec, together with information
85// whether it is using internal or external DTX/CNG.
86const std::map<std::string, CodecTypeAndInfo>& CodecList() {
87 static const auto* const codec_list =
88 new std::map<std::string, CodecTypeAndInfo>{
89 {"opus", {CodecType::kOpus, 111, true}},
90 {"pcmu", {CodecType::kPcmU, 0, false}},
91 {"pcma", {CodecType::kPcmA, 8, false}},
92 {"g722", {CodecType::kG722, 9, false}},
93 {"pcm16b_8", {CodecType::kPcm16b8, 93, false}},
94 {"pcm16b_16", {CodecType::kPcm16b16, 94, false}},
95 {"pcm16b_32", {CodecType::kPcm16b32, 95, false}},
96 {"pcm16b_48", {CodecType::kPcm16b48, 96, false}},
97 {"ilbc", {CodecType::kIlbc, 102, false}},
98 {"isac", {CodecType::kIsac, 103, false}}};
99 return *codec_list;
100}
101
102// This class will receive callbacks from ACM when a packet is ready, and write
103// it to the output file.
104class Packetizer : public AudioPacketizationCallback {
105 public:
106 Packetizer(FILE* out_file, uint32_t ssrc, int timestamp_rate_hz)
107 : out_file_(out_file),
108 ssrc_(ssrc),
109 timestamp_rate_hz_(timestamp_rate_hz) {}
110
Niels Möller87e2d782019-03-07 10:18:23 +0100111 int32_t SendData(AudioFrameType frame_type,
Henrik Lundin1391bd42017-11-24 09:28:57 +0100112 uint8_t payload_type,
113 uint32_t timestamp,
114 const uint8_t* payload_data,
Niels Möllerc35b6e62019-04-25 16:31:18 +0200115 size_t payload_len_bytes) override {
Henrik Lundin32f64d22017-11-28 13:05:35 +0100116 if (payload_len_bytes == 0) {
117 return 0;
118 }
Henrik Lundin1391bd42017-11-24 09:28:57 +0100119
120 constexpr size_t kRtpHeaderLength = 12;
121 constexpr size_t kRtpDumpHeaderLength = 8;
122 const uint16_t length = htons(rtc::checked_cast<uint16_t>(
123 kRtpHeaderLength + kRtpDumpHeaderLength + payload_len_bytes));
124 const uint16_t plen = htons(
125 rtc::checked_cast<uint16_t>(kRtpHeaderLength + payload_len_bytes));
126 const uint32_t offset = htonl(timestamp / (timestamp_rate_hz_ / 1000));
127 RTC_CHECK_EQ(fwrite(&length, sizeof(uint16_t), 1, out_file_), 1);
128 RTC_CHECK_EQ(fwrite(&plen, sizeof(uint16_t), 1, out_file_), 1);
129 RTC_CHECK_EQ(fwrite(&offset, sizeof(uint32_t), 1, out_file_), 1);
130
131 const uint8_t rtp_header[] = {0x80,
132 static_cast<uint8_t>(payload_type & 0x7F),
133 static_cast<uint8_t>(sequence_number_ >> 8),
134 static_cast<uint8_t>(sequence_number_),
135 static_cast<uint8_t>(timestamp >> 24),
136 static_cast<uint8_t>(timestamp >> 16),
137 static_cast<uint8_t>(timestamp >> 8),
138 static_cast<uint8_t>(timestamp),
139 static_cast<uint8_t>(ssrc_ >> 24),
140 static_cast<uint8_t>(ssrc_ >> 16),
141 static_cast<uint8_t>(ssrc_ >> 8),
142 static_cast<uint8_t>(ssrc_)};
143 static_assert(sizeof(rtp_header) == kRtpHeaderLength, "");
144 RTC_CHECK_EQ(
145 fwrite(rtp_header, sizeof(uint8_t), kRtpHeaderLength, out_file_),
146 kRtpHeaderLength);
147 ++sequence_number_; // Intended to wrap on overflow.
148
149 RTC_CHECK_EQ(
150 fwrite(payload_data, sizeof(uint8_t), payload_len_bytes, out_file_),
151 payload_len_bytes);
152
153 return 0;
154 }
155
156 private:
157 FILE* const out_file_;
158 const uint32_t ssrc_;
159 const int timestamp_rate_hz_;
160 uint16_t sequence_number_ = 0;
161};
162
163void SetFrameLenIfFlagIsPositive(int* config_frame_len) {
Mirko Bonadei14be7992019-06-27 15:59:09 +0200164 if (absl::GetFlag(FLAGS_frame_len) > 0) {
165 *config_frame_len = absl::GetFlag(FLAGS_frame_len);
Henrik Lundin1391bd42017-11-24 09:28:57 +0100166 }
167}
168
Henrik Lundinf1061c22017-12-07 10:13:47 +0100169template <typename T>
170typename T::Config GetCodecConfig() {
171 typename T::Config config;
Henrik Lundin1391bd42017-11-24 09:28:57 +0100172 SetFrameLenIfFlagIsPositive(&config.frame_size_ms);
Henrik Lundinf1061c22017-12-07 10:13:47 +0100173 RTC_CHECK(config.IsOk());
174 return config;
175}
176
177AudioEncoderL16::Config Pcm16bConfig(CodecType codec_type) {
178 auto config = GetCodecConfig<AudioEncoderL16>();
Henrik Lundin1391bd42017-11-24 09:28:57 +0100179 switch (codec_type) {
180 case CodecType::kPcm16b8:
181 config.sample_rate_hz = 8000;
182 return config;
183 case CodecType::kPcm16b16:
184 config.sample_rate_hz = 16000;
185 return config;
186 case CodecType::kPcm16b32:
187 config.sample_rate_hz = 32000;
188 return config;
189 case CodecType::kPcm16b48:
190 config.sample_rate_hz = 48000;
191 return config;
192 default:
193 RTC_NOTREACHED();
194 return config;
195 }
196}
197
198std::unique_ptr<AudioEncoder> CreateEncoder(CodecType codec_type,
199 int payload_type) {
200 switch (codec_type) {
201 case CodecType::kOpus: {
Henrik Lundinf1061c22017-12-07 10:13:47 +0100202 AudioEncoderOpus::Config config = GetCodecConfig<AudioEncoderOpus>();
Mirko Bonadei14be7992019-06-27 15:59:09 +0200203 if (absl::GetFlag(FLAGS_bitrate) > 0) {
204 config.bitrate_bps = absl::GetFlag(FLAGS_bitrate);
Henrik Lundin1391bd42017-11-24 09:28:57 +0100205 }
Mirko Bonadei14be7992019-06-27 15:59:09 +0200206 config.dtx_enabled = absl::GetFlag(FLAGS_dtx);
Henrik Lundin1391bd42017-11-24 09:28:57 +0100207 RTC_CHECK(config.IsOk());
208 return AudioEncoderOpus::MakeAudioEncoder(config, payload_type);
209 }
210
211 case CodecType::kPcmU:
212 case CodecType::kPcmA: {
Henrik Lundinf1061c22017-12-07 10:13:47 +0100213 AudioEncoderG711::Config config = GetCodecConfig<AudioEncoderG711>();
Henrik Lundin1391bd42017-11-24 09:28:57 +0100214 config.type = codec_type == CodecType::kPcmU
215 ? AudioEncoderG711::Config::Type::kPcmU
216 : AudioEncoderG711::Config::Type::kPcmA;
217 RTC_CHECK(config.IsOk());
218 return AudioEncoderG711::MakeAudioEncoder(config, payload_type);
219 }
220
221 case CodecType::kG722: {
Henrik Lundinf1061c22017-12-07 10:13:47 +0100222 return AudioEncoderG722::MakeAudioEncoder(
223 GetCodecConfig<AudioEncoderG722>(), payload_type);
Henrik Lundin1391bd42017-11-24 09:28:57 +0100224 }
225
226 case CodecType::kPcm16b8:
227 case CodecType::kPcm16b16:
228 case CodecType::kPcm16b32:
229 case CodecType::kPcm16b48: {
230 return AudioEncoderL16::MakeAudioEncoder(Pcm16bConfig(codec_type),
231 payload_type);
232 }
233
234 case CodecType::kIlbc: {
Henrik Lundinf1061c22017-12-07 10:13:47 +0100235 return AudioEncoderIlbc::MakeAudioEncoder(
236 GetCodecConfig<AudioEncoderIlbc>(), payload_type);
Henrik Lundin1391bd42017-11-24 09:28:57 +0100237 }
238
239 case CodecType::kIsac: {
Henrik Lundinf1061c22017-12-07 10:13:47 +0100240 return AudioEncoderIsac::MakeAudioEncoder(
241 GetCodecConfig<AudioEncoderIsac>(), payload_type);
Henrik Lundin1391bd42017-11-24 09:28:57 +0100242 }
243 }
244 RTC_NOTREACHED();
245 return nullptr;
246}
247
Karl Wiberg23659362018-11-01 11:13:44 +0100248AudioEncoderCngConfig GetCngConfig(int sample_rate_hz) {
249 AudioEncoderCngConfig cng_config;
Henrik Lundin1391bd42017-11-24 09:28:57 +0100250 const auto default_payload_type = [&] {
251 switch (sample_rate_hz) {
Yves Gerey665174f2018-06-19 15:03:05 +0200252 case 8000:
253 return 13;
254 case 16000:
255 return 98;
256 case 32000:
257 return 99;
258 case 48000:
259 return 100;
260 default:
261 RTC_NOTREACHED();
Henrik Lundin1391bd42017-11-24 09:28:57 +0100262 }
263 return 0;
264 };
Mirko Bonadei14be7992019-06-27 15:59:09 +0200265 cng_config.payload_type = absl::GetFlag(FLAGS_cng_payload_type) != -1
266 ? absl::GetFlag(FLAGS_cng_payload_type)
Henrik Lundin1391bd42017-11-24 09:28:57 +0100267 : default_payload_type();
268 return cng_config;
269}
270
271int RunRtpEncode(int argc, char* argv[]) {
Mirko Bonadei14be7992019-06-27 15:59:09 +0200272 std::vector<char*> args = absl::ParseCommandLine(argc, argv);
Henrik Lundin1391bd42017-11-24 09:28:57 +0100273 const std::string usage =
274 "Tool for generating an RTP dump file from audio input.\n"
Mirko Bonadei14be7992019-06-27 15:59:09 +0200275 "Example usage:\n"
276 "./rtp_encode input.pcm output.rtp --codec=[codec] "
Henrik Lundin1391bd42017-11-24 09:28:57 +0100277 "--frame_len=[frame_len] --bitrate=[bitrate]\n\n";
Mirko Bonadei14be7992019-06-27 15:59:09 +0200278 if (!absl::GetFlag(FLAGS_list_codecs) && args.size() != 3) {
Henrik Lundin1391bd42017-11-24 09:28:57 +0100279 printf("%s", usage.c_str());
Henrik Lundin1391bd42017-11-24 09:28:57 +0100280 return 1;
281 }
282
Mirko Bonadei14be7992019-06-27 15:59:09 +0200283 if (absl::GetFlag(FLAGS_list_codecs)) {
Henrik Lundin1391bd42017-11-24 09:28:57 +0100284 printf("The following arguments are valid --codec parameters:\n");
285 for (const auto& c : CodecList()) {
286 printf(" %s\n", c.first.c_str());
287 }
288 return 0;
289 }
290
Mirko Bonadei14be7992019-06-27 15:59:09 +0200291 const auto codec_it = CodecList().find(absl::GetFlag(FLAGS_codec));
Henrik Lundin1391bd42017-11-24 09:28:57 +0100292 if (codec_it == CodecList().end()) {
Mirko Bonadei14be7992019-06-27 15:59:09 +0200293 printf("%s is not a valid codec name.\n",
294 absl::GetFlag(FLAGS_codec).c_str());
Henrik Lundin1391bd42017-11-24 09:28:57 +0100295 printf("Use argument --list_codecs to see all valid codec names.\n");
296 return 1;
297 }
298
299 // Create the codec.
Mirko Bonadei14be7992019-06-27 15:59:09 +0200300 const int payload_type = absl::GetFlag(FLAGS_payload_type) == -1
Henrik Lundin1391bd42017-11-24 09:28:57 +0100301 ? codec_it->second.default_payload_type
Mirko Bonadei14be7992019-06-27 15:59:09 +0200302 : absl::GetFlag(FLAGS_payload_type);
Henrik Lundin1391bd42017-11-24 09:28:57 +0100303 std::unique_ptr<AudioEncoder> codec =
304 CreateEncoder(codec_it->second.type, payload_type);
305
306 // Create an external VAD/CNG encoder if needed.
Mirko Bonadei14be7992019-06-27 15:59:09 +0200307 if (absl::GetFlag(FLAGS_dtx) && !codec_it->second.internal_dtx) {
Karl Wiberg23659362018-11-01 11:13:44 +0100308 AudioEncoderCngConfig cng_config = GetCngConfig(codec->SampleRateHz());
Henrik Lundin1391bd42017-11-24 09:28:57 +0100309 RTC_DCHECK(codec);
310 cng_config.speech_encoder = std::move(codec);
Karl Wiberg23659362018-11-01 11:13:44 +0100311 codec = CreateComfortNoiseEncoder(std::move(cng_config));
Henrik Lundin1391bd42017-11-24 09:28:57 +0100312 }
313 RTC_DCHECK(codec);
314
315 // Set up ACM.
316 const int timestamp_rate_hz = codec->RtpTimestampRateHz();
317 AudioCodingModule::Config config;
318 std::unique_ptr<AudioCodingModule> acm(AudioCodingModule::Create(config));
319 acm->SetEncoder(std::move(codec));
320
321 // Open files.
Mirko Bonadei14be7992019-06-27 15:59:09 +0200322 printf("Input file: %s\n", args[1]);
323 InputAudioFile input_file(args[1], false); // Open input in non-looping mode.
324 FILE* out_file = fopen(args[2], "wb");
325 RTC_CHECK(out_file) << "Could not open file " << args[2] << " for writing";
326 printf("Output file: %s\n", args[2]);
Henrik Lundin1391bd42017-11-24 09:28:57 +0100327 fprintf(out_file, "#!rtpplay1.0 \n"); //,
328 // Write 3 32-bit values followed by 2 16-bit values, all set to 0. This means
329 // a total of 16 bytes.
330 const uint8_t file_header[16] = {0};
331 RTC_CHECK_EQ(fwrite(file_header, sizeof(file_header), 1, out_file), 1);
332
333 // Create and register the packetizer, which will write the packets to file.
Mirko Bonadei14be7992019-06-27 15:59:09 +0200334 Packetizer packetizer(out_file, absl::GetFlag(FLAGS_ssrc), timestamp_rate_hz);
Henrik Lundin1391bd42017-11-24 09:28:57 +0100335 RTC_DCHECK_EQ(acm->RegisterTransportCallback(&packetizer), 0);
336
337 AudioFrame audio_frame;
Mirko Bonadei14be7992019-06-27 15:59:09 +0200338 audio_frame.samples_per_channel_ =
339 absl::GetFlag(FLAGS_sample_rate) / 100; // 10 ms
340 audio_frame.sample_rate_hz_ = absl::GetFlag(FLAGS_sample_rate);
Henrik Lundin1391bd42017-11-24 09:28:57 +0100341 audio_frame.num_channels_ = 1;
342
343 while (input_file.Read(audio_frame.samples_per_channel_,
344 audio_frame.mutable_data())) {
345 RTC_CHECK_GE(acm->Add10MsData(audio_frame), 0);
346 audio_frame.timestamp_ += audio_frame.samples_per_channel_;
347 }
348
349 return 0;
350}
351
352} // namespace
353} // namespace test
354} // namespace webrtc
355
356int main(int argc, char* argv[]) {
357 return webrtc::test::RunRtpEncode(argc, argv);
358}