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