• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #ifdef WEBRTC_LINUX
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/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"
38 #include "rtc_base/numerics/safe_conversions.h"
39 
40 ABSL_FLAG(bool, list_codecs, false, "Enumerate all codecs");
41 ABSL_FLAG(std::string, codec, "opus", "Codec to use");
42 ABSL_FLAG(int,
43           frame_len,
44           0,
45           "Frame length in ms; 0 indicates codec default value");
46 ABSL_FLAG(int, bitrate, 0, "Bitrate in kbps; 0 indicates codec default value");
47 ABSL_FLAG(int,
48           payload_type,
49           -1,
50           "RTP payload type; -1 indicates codec default value");
51 ABSL_FLAG(int,
52           cng_payload_type,
53           -1,
54           "RTP payload type for CNG; -1 indicates default value");
55 ABSL_FLAG(int, ssrc, 0, "SSRC to write to the RTP header");
56 ABSL_FLAG(bool, dtx, false, "Use DTX/CNG");
57 ABSL_FLAG(int, sample_rate, 48000, "Sample rate of the input file");
58 
59 namespace webrtc {
60 namespace test {
61 namespace {
62 
63 // Add new codecs here, and to the map below.
64 enum class CodecType {
65   kOpus,
66   kPcmU,
67   kPcmA,
68   kG722,
69   kPcm16b8,
70   kPcm16b16,
71   kPcm16b32,
72   kPcm16b48,
73   kIlbc,
74   kIsac
75 };
76 
77 struct 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.
CodecList()86 const 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.
104 class Packetizer : public AudioPacketizationCallback {
105  public:
Packetizer(FILE * out_file,uint32_t ssrc,int timestamp_rate_hz)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 
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)111   int32_t SendData(AudioFrameType frame_type,
112                    uint8_t payload_type,
113                    uint32_t timestamp,
114                    const uint8_t* payload_data,
115                    size_t payload_len_bytes,
116                    int64_t absolute_capture_timestamp_ms) override {
117     if (payload_len_bytes == 0) {
118       return 0;
119     }
120 
121     constexpr size_t kRtpHeaderLength = 12;
122     constexpr size_t kRtpDumpHeaderLength = 8;
123     const uint16_t length = htons(rtc::checked_cast<uint16_t>(
124         kRtpHeaderLength + kRtpDumpHeaderLength + payload_len_bytes));
125     const uint16_t plen = htons(
126         rtc::checked_cast<uint16_t>(kRtpHeaderLength + payload_len_bytes));
127     const uint32_t offset = htonl(timestamp / (timestamp_rate_hz_ / 1000));
128     RTC_CHECK_EQ(fwrite(&length, sizeof(uint16_t), 1, out_file_), 1);
129     RTC_CHECK_EQ(fwrite(&plen, sizeof(uint16_t), 1, out_file_), 1);
130     RTC_CHECK_EQ(fwrite(&offset, sizeof(uint32_t), 1, out_file_), 1);
131 
132     const uint8_t rtp_header[] = {0x80,
133                                   static_cast<uint8_t>(payload_type & 0x7F),
134                                   static_cast<uint8_t>(sequence_number_ >> 8),
135                                   static_cast<uint8_t>(sequence_number_),
136                                   static_cast<uint8_t>(timestamp >> 24),
137                                   static_cast<uint8_t>(timestamp >> 16),
138                                   static_cast<uint8_t>(timestamp >> 8),
139                                   static_cast<uint8_t>(timestamp),
140                                   static_cast<uint8_t>(ssrc_ >> 24),
141                                   static_cast<uint8_t>(ssrc_ >> 16),
142                                   static_cast<uint8_t>(ssrc_ >> 8),
143                                   static_cast<uint8_t>(ssrc_)};
144     static_assert(sizeof(rtp_header) == kRtpHeaderLength, "");
145     RTC_CHECK_EQ(
146         fwrite(rtp_header, sizeof(uint8_t), kRtpHeaderLength, out_file_),
147         kRtpHeaderLength);
148     ++sequence_number_;  // Intended to wrap on overflow.
149 
150     RTC_CHECK_EQ(
151         fwrite(payload_data, sizeof(uint8_t), payload_len_bytes, out_file_),
152         payload_len_bytes);
153 
154     return 0;
155   }
156 
157  private:
158   FILE* const out_file_;
159   const uint32_t ssrc_;
160   const int timestamp_rate_hz_;
161   uint16_t sequence_number_ = 0;
162 };
163 
SetFrameLenIfFlagIsPositive(int * config_frame_len)164 void SetFrameLenIfFlagIsPositive(int* config_frame_len) {
165   if (absl::GetFlag(FLAGS_frame_len) > 0) {
166     *config_frame_len = absl::GetFlag(FLAGS_frame_len);
167   }
168 }
169 
170 template <typename T>
GetCodecConfig()171 typename T::Config GetCodecConfig() {
172   typename T::Config config;
173   SetFrameLenIfFlagIsPositive(&config.frame_size_ms);
174   RTC_CHECK(config.IsOk());
175   return config;
176 }
177 
Pcm16bConfig(CodecType codec_type)178 AudioEncoderL16::Config Pcm16bConfig(CodecType codec_type) {
179   auto config = GetCodecConfig<AudioEncoderL16>();
180   switch (codec_type) {
181     case CodecType::kPcm16b8:
182       config.sample_rate_hz = 8000;
183       return config;
184     case CodecType::kPcm16b16:
185       config.sample_rate_hz = 16000;
186       return config;
187     case CodecType::kPcm16b32:
188       config.sample_rate_hz = 32000;
189       return config;
190     case CodecType::kPcm16b48:
191       config.sample_rate_hz = 48000;
192       return config;
193     default:
194       RTC_NOTREACHED();
195       return config;
196   }
197 }
198 
CreateEncoder(CodecType codec_type,int payload_type)199 std::unique_ptr<AudioEncoder> CreateEncoder(CodecType codec_type,
200                                             int payload_type) {
201   switch (codec_type) {
202     case CodecType::kOpus: {
203       AudioEncoderOpus::Config config = GetCodecConfig<AudioEncoderOpus>();
204       if (absl::GetFlag(FLAGS_bitrate) > 0) {
205         config.bitrate_bps = absl::GetFlag(FLAGS_bitrate);
206       }
207       config.dtx_enabled = absl::GetFlag(FLAGS_dtx);
208       RTC_CHECK(config.IsOk());
209       return AudioEncoderOpus::MakeAudioEncoder(config, payload_type);
210     }
211 
212     case CodecType::kPcmU:
213     case CodecType::kPcmA: {
214       AudioEncoderG711::Config config = GetCodecConfig<AudioEncoderG711>();
215       config.type = codec_type == CodecType::kPcmU
216                         ? AudioEncoderG711::Config::Type::kPcmU
217                         : AudioEncoderG711::Config::Type::kPcmA;
218       RTC_CHECK(config.IsOk());
219       return AudioEncoderG711::MakeAudioEncoder(config, payload_type);
220     }
221 
222     case CodecType::kG722: {
223       return AudioEncoderG722::MakeAudioEncoder(
224           GetCodecConfig<AudioEncoderG722>(), payload_type);
225     }
226 
227     case CodecType::kPcm16b8:
228     case CodecType::kPcm16b16:
229     case CodecType::kPcm16b32:
230     case CodecType::kPcm16b48: {
231       return AudioEncoderL16::MakeAudioEncoder(Pcm16bConfig(codec_type),
232                                                payload_type);
233     }
234 
235     case CodecType::kIlbc: {
236       return AudioEncoderIlbc::MakeAudioEncoder(
237           GetCodecConfig<AudioEncoderIlbc>(), payload_type);
238     }
239 
240     case CodecType::kIsac: {
241       return AudioEncoderIsac::MakeAudioEncoder(
242           GetCodecConfig<AudioEncoderIsac>(), payload_type);
243     }
244   }
245   RTC_NOTREACHED();
246   return nullptr;
247 }
248 
GetCngConfig(int sample_rate_hz)249 AudioEncoderCngConfig GetCngConfig(int sample_rate_hz) {
250   AudioEncoderCngConfig cng_config;
251   const auto default_payload_type = [&] {
252     switch (sample_rate_hz) {
253       case 8000:
254         return 13;
255       case 16000:
256         return 98;
257       case 32000:
258         return 99;
259       case 48000:
260         return 100;
261       default:
262         RTC_NOTREACHED();
263     }
264     return 0;
265   };
266   cng_config.payload_type = absl::GetFlag(FLAGS_cng_payload_type) != -1
267                                 ? absl::GetFlag(FLAGS_cng_payload_type)
268                                 : default_payload_type();
269   return cng_config;
270 }
271 
RunRtpEncode(int argc,char * argv[])272 int RunRtpEncode(int argc, char* argv[]) {
273   std::vector<char*> args = absl::ParseCommandLine(argc, argv);
274   const std::string usage =
275       "Tool for generating an RTP dump file from audio input.\n"
276       "Example usage:\n"
277       "./rtp_encode input.pcm output.rtp --codec=[codec] "
278       "--frame_len=[frame_len] --bitrate=[bitrate]\n\n";
279   if (!absl::GetFlag(FLAGS_list_codecs) && args.size() != 3) {
280     printf("%s", usage.c_str());
281     return 1;
282   }
283 
284   if (absl::GetFlag(FLAGS_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(absl::GetFlag(FLAGS_codec));
293   if (codec_it == CodecList().end()) {
294     printf("%s is not a valid codec name.\n",
295            absl::GetFlag(FLAGS_codec).c_str());
296     printf("Use argument --list_codecs to see all valid codec names.\n");
297     return 1;
298   }
299 
300   // Create the codec.
301   const int payload_type = absl::GetFlag(FLAGS_payload_type) == -1
302                                ? codec_it->second.default_payload_type
303                                : absl::GetFlag(FLAGS_payload_type);
304   std::unique_ptr<AudioEncoder> codec =
305       CreateEncoder(codec_it->second.type, payload_type);
306 
307   // Create an external VAD/CNG encoder if needed.
308   if (absl::GetFlag(FLAGS_dtx) && !codec_it->second.internal_dtx) {
309     AudioEncoderCngConfig cng_config = GetCngConfig(codec->SampleRateHz());
310     RTC_DCHECK(codec);
311     cng_config.speech_encoder = std::move(codec);
312     codec = CreateComfortNoiseEncoder(std::move(cng_config));
313   }
314   RTC_DCHECK(codec);
315 
316   // Set up ACM.
317   const int timestamp_rate_hz = codec->RtpTimestampRateHz();
318   AudioCodingModule::Config config;
319   std::unique_ptr<AudioCodingModule> acm(AudioCodingModule::Create(config));
320   acm->SetEncoder(std::move(codec));
321 
322   // Open files.
323   printf("Input file: %s\n", args[1]);
324   InputAudioFile input_file(args[1], false);  // Open input in non-looping mode.
325   FILE* out_file = fopen(args[2], "wb");
326   RTC_CHECK(out_file) << "Could not open file " << args[2] << " for writing";
327   printf("Output file: %s\n", args[2]);
328   fprintf(out_file, "#!rtpplay1.0 \n");  //,
329   // Write 3 32-bit values followed by 2 16-bit values, all set to 0. This means
330   // a total of 16 bytes.
331   const uint8_t file_header[16] = {0};
332   RTC_CHECK_EQ(fwrite(file_header, sizeof(file_header), 1, out_file), 1);
333 
334   // Create and register the packetizer, which will write the packets to file.
335   Packetizer packetizer(out_file, absl::GetFlag(FLAGS_ssrc), timestamp_rate_hz);
336   RTC_DCHECK_EQ(acm->RegisterTransportCallback(&packetizer), 0);
337 
338   AudioFrame audio_frame;
339   audio_frame.samples_per_channel_ =
340       absl::GetFlag(FLAGS_sample_rate) / 100;  // 10 ms
341   audio_frame.sample_rate_hz_ = absl::GetFlag(FLAGS_sample_rate);
342   audio_frame.num_channels_ = 1;
343 
344   while (input_file.Read(audio_frame.samples_per_channel_,
345                          audio_frame.mutable_data())) {
346     RTC_CHECK_GE(acm->Add10MsData(audio_frame), 0);
347     audio_frame.timestamp_ += audio_frame.samples_per_channel_;
348   }
349 
350   return 0;
351 }
352 
353 }  // namespace
354 }  // namespace test
355 }  // namespace webrtc
356 
main(int argc,char * argv[])357 int main(int argc, char* argv[]) {
358   return webrtc::test::RunRtpEncode(argc, argv);
359 }
360