blob: a75802bfb2039f25a756350edb47c51cda9e89ba [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>
23
Karl Wiberg918f50c2018-07-05 11:40:33 +020024#include "absl/memory/memory.h"
Fredrik Solenbergbbf21a32018-04-12 22:44:09 +020025#include "api/audio/audio_frame.h"
Henrik Lundin1391bd42017-11-24 09:28:57 +010026#include "api/audio_codecs/L16/audio_encoder_L16.h"
27#include "api/audio_codecs/g711/audio_encoder_g711.h"
28#include "api/audio_codecs/g722/audio_encoder_g722.h"
29#include "api/audio_codecs/ilbc/audio_encoder_ilbc.h"
30#include "api/audio_codecs/isac/audio_encoder_isac.h"
31#include "api/audio_codecs/opus/audio_encoder_opus.h"
32#include "modules/audio_coding/codecs/cng/audio_encoder_cng.h"
33#include "modules/audio_coding/include/audio_coding_module.h"
34#include "modules/audio_coding/neteq/tools/input_audio_file.h"
35#include "rtc_base/flags.h"
36#include "rtc_base/numerics/safe_conversions.h"
Henrik Lundin1391bd42017-11-24 09:28:57 +010037
38namespace webrtc {
39namespace test {
40namespace {
41
42// Define command line flags.
Mirko Bonadei2dfa9982018-10-18 11:35:32 +020043WEBRTC_DEFINE_bool(list_codecs, false, "Enumerate all codecs");
44WEBRTC_DEFINE_string(codec, "opus", "Codec to use");
45WEBRTC_DEFINE_int(frame_len,
46 0,
47 "Frame length in ms; 0 indicates codec default value");
48WEBRTC_DEFINE_int(bitrate,
49 0,
50 "Bitrate in kbps; 0 indicates codec default value");
51WEBRTC_DEFINE_int(payload_type,
52 -1,
53 "RTP payload type; -1 indicates codec default value");
54WEBRTC_DEFINE_int(cng_payload_type,
55 -1,
56 "RTP payload type for CNG; -1 indicates default value");
57WEBRTC_DEFINE_int(ssrc, 0, "SSRC to write to the RTP header");
58WEBRTC_DEFINE_bool(dtx, false, "Use DTX/CNG");
59WEBRTC_DEFINE_int(sample_rate, 48000, "Sample rate of the input file");
60WEBRTC_DEFINE_bool(help, false, "Print this message");
Henrik Lundin1391bd42017-11-24 09:28:57 +010061
62// Add new codecs here, and to the map below.
63enum class CodecType {
64 kOpus,
65 kPcmU,
66 kPcmA,
67 kG722,
68 kPcm16b8,
69 kPcm16b16,
70 kPcm16b32,
71 kPcm16b48,
72 kIlbc,
73 kIsac
74};
75
76struct CodecTypeAndInfo {
77 CodecType type;
78 int default_payload_type;
79 bool internal_dtx;
80};
81
82// List all supported codecs here. This map defines the command-line parameter
83// value (the key string) for selecting each codec, together with information
84// whether it is using internal or external DTX/CNG.
85const std::map<std::string, CodecTypeAndInfo>& CodecList() {
86 static const auto* const codec_list =
87 new std::map<std::string, CodecTypeAndInfo>{
88 {"opus", {CodecType::kOpus, 111, true}},
89 {"pcmu", {CodecType::kPcmU, 0, false}},
90 {"pcma", {CodecType::kPcmA, 8, false}},
91 {"g722", {CodecType::kG722, 9, false}},
92 {"pcm16b_8", {CodecType::kPcm16b8, 93, false}},
93 {"pcm16b_16", {CodecType::kPcm16b16, 94, false}},
94 {"pcm16b_32", {CodecType::kPcm16b32, 95, false}},
95 {"pcm16b_48", {CodecType::kPcm16b48, 96, false}},
96 {"ilbc", {CodecType::kIlbc, 102, false}},
97 {"isac", {CodecType::kIsac, 103, false}}};
98 return *codec_list;
99}
100
101// This class will receive callbacks from ACM when a packet is ready, and write
102// it to the output file.
103class Packetizer : public AudioPacketizationCallback {
104 public:
105 Packetizer(FILE* out_file, uint32_t ssrc, int timestamp_rate_hz)
106 : out_file_(out_file),
107 ssrc_(ssrc),
108 timestamp_rate_hz_(timestamp_rate_hz) {}
109
Niels Möller87e2d782019-03-07 10:18:23 +0100110 int32_t SendData(AudioFrameType frame_type,
Henrik Lundin1391bd42017-11-24 09:28:57 +0100111 uint8_t payload_type,
112 uint32_t timestamp,
113 const uint8_t* payload_data,
Niels Möllerc35b6e62019-04-25 16:31:18 +0200114 size_t payload_len_bytes) override {
Henrik Lundin32f64d22017-11-28 13:05:35 +0100115 if (payload_len_bytes == 0) {
116 return 0;
117 }
Henrik Lundin1391bd42017-11-24 09:28:57 +0100118
119 constexpr size_t kRtpHeaderLength = 12;
120 constexpr size_t kRtpDumpHeaderLength = 8;
121 const uint16_t length = htons(rtc::checked_cast<uint16_t>(
122 kRtpHeaderLength + kRtpDumpHeaderLength + payload_len_bytes));
123 const uint16_t plen = htons(
124 rtc::checked_cast<uint16_t>(kRtpHeaderLength + payload_len_bytes));
125 const uint32_t offset = htonl(timestamp / (timestamp_rate_hz_ / 1000));
126 RTC_CHECK_EQ(fwrite(&length, sizeof(uint16_t), 1, out_file_), 1);
127 RTC_CHECK_EQ(fwrite(&plen, sizeof(uint16_t), 1, out_file_), 1);
128 RTC_CHECK_EQ(fwrite(&offset, sizeof(uint32_t), 1, out_file_), 1);
129
130 const uint8_t rtp_header[] = {0x80,
131 static_cast<uint8_t>(payload_type & 0x7F),
132 static_cast<uint8_t>(sequence_number_ >> 8),
133 static_cast<uint8_t>(sequence_number_),
134 static_cast<uint8_t>(timestamp >> 24),
135 static_cast<uint8_t>(timestamp >> 16),
136 static_cast<uint8_t>(timestamp >> 8),
137 static_cast<uint8_t>(timestamp),
138 static_cast<uint8_t>(ssrc_ >> 24),
139 static_cast<uint8_t>(ssrc_ >> 16),
140 static_cast<uint8_t>(ssrc_ >> 8),
141 static_cast<uint8_t>(ssrc_)};
142 static_assert(sizeof(rtp_header) == kRtpHeaderLength, "");
143 RTC_CHECK_EQ(
144 fwrite(rtp_header, sizeof(uint8_t), kRtpHeaderLength, out_file_),
145 kRtpHeaderLength);
146 ++sequence_number_; // Intended to wrap on overflow.
147
148 RTC_CHECK_EQ(
149 fwrite(payload_data, sizeof(uint8_t), payload_len_bytes, out_file_),
150 payload_len_bytes);
151
152 return 0;
153 }
154
155 private:
156 FILE* const out_file_;
157 const uint32_t ssrc_;
158 const int timestamp_rate_hz_;
159 uint16_t sequence_number_ = 0;
160};
161
162void SetFrameLenIfFlagIsPositive(int* config_frame_len) {
163 if (FLAG_frame_len > 0) {
164 *config_frame_len = FLAG_frame_len;
165 }
166}
167
Henrik Lundinf1061c22017-12-07 10:13:47 +0100168template <typename T>
169typename T::Config GetCodecConfig() {
170 typename T::Config config;
Henrik Lundin1391bd42017-11-24 09:28:57 +0100171 SetFrameLenIfFlagIsPositive(&config.frame_size_ms);
Henrik Lundinf1061c22017-12-07 10:13:47 +0100172 RTC_CHECK(config.IsOk());
173 return config;
174}
175
176AudioEncoderL16::Config Pcm16bConfig(CodecType codec_type) {
177 auto config = GetCodecConfig<AudioEncoderL16>();
Henrik Lundin1391bd42017-11-24 09:28:57 +0100178 switch (codec_type) {
179 case CodecType::kPcm16b8:
180 config.sample_rate_hz = 8000;
181 return config;
182 case CodecType::kPcm16b16:
183 config.sample_rate_hz = 16000;
184 return config;
185 case CodecType::kPcm16b32:
186 config.sample_rate_hz = 32000;
187 return config;
188 case CodecType::kPcm16b48:
189 config.sample_rate_hz = 48000;
190 return config;
191 default:
192 RTC_NOTREACHED();
193 return config;
194 }
195}
196
197std::unique_ptr<AudioEncoder> CreateEncoder(CodecType codec_type,
198 int payload_type) {
199 switch (codec_type) {
200 case CodecType::kOpus: {
Henrik Lundinf1061c22017-12-07 10:13:47 +0100201 AudioEncoderOpus::Config config = GetCodecConfig<AudioEncoderOpus>();
Henrik Lundin1391bd42017-11-24 09:28:57 +0100202 if (FLAG_bitrate > 0) {
203 config.bitrate_bps = FLAG_bitrate;
204 }
205 config.dtx_enabled = FLAG_dtx;
Henrik Lundin1391bd42017-11-24 09:28:57 +0100206 RTC_CHECK(config.IsOk());
207 return AudioEncoderOpus::MakeAudioEncoder(config, payload_type);
208 }
209
210 case CodecType::kPcmU:
211 case CodecType::kPcmA: {
Henrik Lundinf1061c22017-12-07 10:13:47 +0100212 AudioEncoderG711::Config config = GetCodecConfig<AudioEncoderG711>();
Henrik Lundin1391bd42017-11-24 09:28:57 +0100213 config.type = codec_type == CodecType::kPcmU
214 ? AudioEncoderG711::Config::Type::kPcmU
215 : AudioEncoderG711::Config::Type::kPcmA;
216 RTC_CHECK(config.IsOk());
217 return AudioEncoderG711::MakeAudioEncoder(config, payload_type);
218 }
219
220 case CodecType::kG722: {
Henrik Lundinf1061c22017-12-07 10:13:47 +0100221 return AudioEncoderG722::MakeAudioEncoder(
222 GetCodecConfig<AudioEncoderG722>(), payload_type);
Henrik Lundin1391bd42017-11-24 09:28:57 +0100223 }
224
225 case CodecType::kPcm16b8:
226 case CodecType::kPcm16b16:
227 case CodecType::kPcm16b32:
228 case CodecType::kPcm16b48: {
229 return AudioEncoderL16::MakeAudioEncoder(Pcm16bConfig(codec_type),
230 payload_type);
231 }
232
233 case CodecType::kIlbc: {
Henrik Lundinf1061c22017-12-07 10:13:47 +0100234 return AudioEncoderIlbc::MakeAudioEncoder(
235 GetCodecConfig<AudioEncoderIlbc>(), payload_type);
Henrik Lundin1391bd42017-11-24 09:28:57 +0100236 }
237
238 case CodecType::kIsac: {
Henrik Lundinf1061c22017-12-07 10:13:47 +0100239 return AudioEncoderIsac::MakeAudioEncoder(
240 GetCodecConfig<AudioEncoderIsac>(), payload_type);
Henrik Lundin1391bd42017-11-24 09:28:57 +0100241 }
242 }
243 RTC_NOTREACHED();
244 return nullptr;
245}
246
Karl Wiberg23659362018-11-01 11:13:44 +0100247AudioEncoderCngConfig GetCngConfig(int sample_rate_hz) {
248 AudioEncoderCngConfig cng_config;
Henrik Lundin1391bd42017-11-24 09:28:57 +0100249 const auto default_payload_type = [&] {
250 switch (sample_rate_hz) {
Yves Gerey665174f2018-06-19 15:03:05 +0200251 case 8000:
252 return 13;
253 case 16000:
254 return 98;
255 case 32000:
256 return 99;
257 case 48000:
258 return 100;
259 default:
260 RTC_NOTREACHED();
Henrik Lundin1391bd42017-11-24 09:28:57 +0100261 }
262 return 0;
263 };
264 cng_config.payload_type = FLAG_cng_payload_type != -1
265 ? FLAG_cng_payload_type
266 : default_payload_type();
267 return cng_config;
268}
269
270int RunRtpEncode(int argc, char* argv[]) {
271 const std::string program_name = argv[0];
272 const std::string usage =
273 "Tool for generating an RTP dump file from audio input.\n"
274 "Run " +
275 program_name +
276 " --help for usage.\n"
277 "Example usage:\n" +
278 program_name + " input.pcm output.rtp --codec=[codec] " +
279 "--frame_len=[frame_len] --bitrate=[bitrate]\n\n";
280 if (rtc::FlagList::SetFlagsFromCommandLine(&argc, argv, true) || FLAG_help ||
281 (!FLAG_list_codecs && argc != 3)) {
282 printf("%s", usage.c_str());
283 if (FLAG_help) {
284 rtc::FlagList::Print(nullptr, false);
285 return 0;
286 }
287 return 1;
288 }
289
290 if (FLAG_list_codecs) {
291 printf("The following arguments are valid --codec parameters:\n");
292 for (const auto& c : CodecList()) {
293 printf(" %s\n", c.first.c_str());
294 }
295 return 0;
296 }
297
298 const auto codec_it = CodecList().find(FLAG_codec);
299 if (codec_it == CodecList().end()) {
300 printf("%s is not a valid codec name.\n", FLAG_codec);
301 printf("Use argument --list_codecs to see all valid codec names.\n");
302 return 1;
303 }
304
305 // Create the codec.
306 const int payload_type = FLAG_payload_type == -1
307 ? codec_it->second.default_payload_type
308 : FLAG_payload_type;
309 std::unique_ptr<AudioEncoder> codec =
310 CreateEncoder(codec_it->second.type, payload_type);
311
312 // Create an external VAD/CNG encoder if needed.
Henrik Lundin32f64d22017-11-28 13:05:35 +0100313 if (FLAG_dtx && !codec_it->second.internal_dtx) {
Karl Wiberg23659362018-11-01 11:13:44 +0100314 AudioEncoderCngConfig cng_config = GetCngConfig(codec->SampleRateHz());
Henrik Lundin1391bd42017-11-24 09:28:57 +0100315 RTC_DCHECK(codec);
316 cng_config.speech_encoder = std::move(codec);
Karl Wiberg23659362018-11-01 11:13:44 +0100317 codec = CreateComfortNoiseEncoder(std::move(cng_config));
Henrik Lundin1391bd42017-11-24 09:28:57 +0100318 }
319 RTC_DCHECK(codec);
320
321 // Set up ACM.
322 const int timestamp_rate_hz = codec->RtpTimestampRateHz();
323 AudioCodingModule::Config config;
324 std::unique_ptr<AudioCodingModule> acm(AudioCodingModule::Create(config));
325 acm->SetEncoder(std::move(codec));
326
327 // Open files.
328 printf("Input file: %s\n", argv[1]);
329 InputAudioFile input_file(argv[1], false); // Open input in non-looping mode.
330 FILE* out_file = fopen(argv[2], "wb");
331 RTC_CHECK(out_file) << "Could not open file " << argv[2] << " for writing";
332 printf("Output file: %s\n", argv[2]);
333 fprintf(out_file, "#!rtpplay1.0 \n"); //,
334 // Write 3 32-bit values followed by 2 16-bit values, all set to 0. This means
335 // a total of 16 bytes.
336 const uint8_t file_header[16] = {0};
337 RTC_CHECK_EQ(fwrite(file_header, sizeof(file_header), 1, out_file), 1);
338
339 // Create and register the packetizer, which will write the packets to file.
340 Packetizer packetizer(out_file, FLAG_ssrc, timestamp_rate_hz);
341 RTC_DCHECK_EQ(acm->RegisterTransportCallback(&packetizer), 0);
342
343 AudioFrame audio_frame;
344 audio_frame.samples_per_channel_ = FLAG_sample_rate / 100; // 10 ms
345 audio_frame.sample_rate_hz_ = FLAG_sample_rate;
346 audio_frame.num_channels_ = 1;
347
348 while (input_file.Read(audio_frame.samples_per_channel_,
349 audio_frame.mutable_data())) {
350 RTC_CHECK_GE(acm->Add10MsData(audio_frame), 0);
351 audio_frame.timestamp_ += audio_frame.samples_per_channel_;
352 }
353
354 return 0;
355}
356
357} // namespace
358} // namespace test
359} // namespace webrtc
360
361int main(int argc, char* argv[]) {
362 return webrtc::test::RunRtpEncode(argc, argv);
363}