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