Henrik Lundin | 1391bd4 | 2017-11-24 09:28:57 +0100 | [diff] [blame] | 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> |
Mirko Bonadei | 14be799 | 2019-06-27 15:59:09 +0200 | [diff] [blame] | 23 | #include <vector> |
Henrik Lundin | 1391bd4 | 2017-11-24 09:28:57 +0100 | [diff] [blame] | 24 | |
Mirko Bonadei | 14be799 | 2019-06-27 15:59:09 +0200 | [diff] [blame] | 25 | #include "absl/flags/flag.h" |
| 26 | #include "absl/flags/parse.h" |
Karl Wiberg | 918f50c | 2018-07-05 11:40:33 +0200 | [diff] [blame] | 27 | #include "absl/memory/memory.h" |
Fredrik Solenberg | bbf21a3 | 2018-04-12 22:44:09 +0200 | [diff] [blame] | 28 | #include "api/audio/audio_frame.h" |
Henrik Lundin | 1391bd4 | 2017-11-24 09:28:57 +0100 | [diff] [blame] | 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" |
Henrik Lundin | 1391bd4 | 2017-11-24 09:28:57 +0100 | [diff] [blame] | 38 | #include "rtc_base/numerics/safe_conversions.h" |
Henrik Lundin | 1391bd4 | 2017-11-24 09:28:57 +0100 | [diff] [blame] | 39 | |
Mirko Bonadei | 14be799 | 2019-06-27 15:59:09 +0200 | [diff] [blame] | 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 | |
Henrik Lundin | 1391bd4 | 2017-11-24 09:28:57 +0100 | [diff] [blame] | 59 | namespace webrtc { |
| 60 | namespace test { |
| 61 | namespace { |
| 62 | |
Henrik Lundin | 1391bd4 | 2017-11-24 09:28:57 +0100 | [diff] [blame] | 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. |
| 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: |
| 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öller | 87e2d78 | 2019-03-07 10:18:23 +0100 | [diff] [blame] | 111 | int32_t SendData(AudioFrameType frame_type, |
Henrik Lundin | 1391bd4 | 2017-11-24 09:28:57 +0100 | [diff] [blame] | 112 | uint8_t payload_type, |
| 113 | uint32_t timestamp, |
| 114 | const uint8_t* payload_data, |
Niels Möller | c35b6e6 | 2019-04-25 16:31:18 +0200 | [diff] [blame] | 115 | size_t payload_len_bytes) override { |
Henrik Lundin | 32f64d2 | 2017-11-28 13:05:35 +0100 | [diff] [blame] | 116 | if (payload_len_bytes == 0) { |
| 117 | return 0; |
| 118 | } |
Henrik Lundin | 1391bd4 | 2017-11-24 09:28:57 +0100 | [diff] [blame] | 119 | |
| 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 | |
| 163 | void SetFrameLenIfFlagIsPositive(int* config_frame_len) { |
Mirko Bonadei | 14be799 | 2019-06-27 15:59:09 +0200 | [diff] [blame] | 164 | if (absl::GetFlag(FLAGS_frame_len) > 0) { |
| 165 | *config_frame_len = absl::GetFlag(FLAGS_frame_len); |
Henrik Lundin | 1391bd4 | 2017-11-24 09:28:57 +0100 | [diff] [blame] | 166 | } |
| 167 | } |
| 168 | |
Henrik Lundin | f1061c2 | 2017-12-07 10:13:47 +0100 | [diff] [blame] | 169 | template <typename T> |
| 170 | typename T::Config GetCodecConfig() { |
| 171 | typename T::Config config; |
Henrik Lundin | 1391bd4 | 2017-11-24 09:28:57 +0100 | [diff] [blame] | 172 | SetFrameLenIfFlagIsPositive(&config.frame_size_ms); |
Henrik Lundin | f1061c2 | 2017-12-07 10:13:47 +0100 | [diff] [blame] | 173 | RTC_CHECK(config.IsOk()); |
| 174 | return config; |
| 175 | } |
| 176 | |
| 177 | AudioEncoderL16::Config Pcm16bConfig(CodecType codec_type) { |
| 178 | auto config = GetCodecConfig<AudioEncoderL16>(); |
Henrik Lundin | 1391bd4 | 2017-11-24 09:28:57 +0100 | [diff] [blame] | 179 | 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 | |
| 198 | std::unique_ptr<AudioEncoder> CreateEncoder(CodecType codec_type, |
| 199 | int payload_type) { |
| 200 | switch (codec_type) { |
| 201 | case CodecType::kOpus: { |
Henrik Lundin | f1061c2 | 2017-12-07 10:13:47 +0100 | [diff] [blame] | 202 | AudioEncoderOpus::Config config = GetCodecConfig<AudioEncoderOpus>(); |
Mirko Bonadei | 14be799 | 2019-06-27 15:59:09 +0200 | [diff] [blame] | 203 | if (absl::GetFlag(FLAGS_bitrate) > 0) { |
| 204 | config.bitrate_bps = absl::GetFlag(FLAGS_bitrate); |
Henrik Lundin | 1391bd4 | 2017-11-24 09:28:57 +0100 | [diff] [blame] | 205 | } |
Mirko Bonadei | 14be799 | 2019-06-27 15:59:09 +0200 | [diff] [blame] | 206 | config.dtx_enabled = absl::GetFlag(FLAGS_dtx); |
Henrik Lundin | 1391bd4 | 2017-11-24 09:28:57 +0100 | [diff] [blame] | 207 | RTC_CHECK(config.IsOk()); |
| 208 | return AudioEncoderOpus::MakeAudioEncoder(config, payload_type); |
| 209 | } |
| 210 | |
| 211 | case CodecType::kPcmU: |
| 212 | case CodecType::kPcmA: { |
Henrik Lundin | f1061c2 | 2017-12-07 10:13:47 +0100 | [diff] [blame] | 213 | AudioEncoderG711::Config config = GetCodecConfig<AudioEncoderG711>(); |
Henrik Lundin | 1391bd4 | 2017-11-24 09:28:57 +0100 | [diff] [blame] | 214 | 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 Lundin | f1061c2 | 2017-12-07 10:13:47 +0100 | [diff] [blame] | 222 | return AudioEncoderG722::MakeAudioEncoder( |
| 223 | GetCodecConfig<AudioEncoderG722>(), payload_type); |
Henrik Lundin | 1391bd4 | 2017-11-24 09:28:57 +0100 | [diff] [blame] | 224 | } |
| 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 Lundin | f1061c2 | 2017-12-07 10:13:47 +0100 | [diff] [blame] | 235 | return AudioEncoderIlbc::MakeAudioEncoder( |
| 236 | GetCodecConfig<AudioEncoderIlbc>(), payload_type); |
Henrik Lundin | 1391bd4 | 2017-11-24 09:28:57 +0100 | [diff] [blame] | 237 | } |
| 238 | |
| 239 | case CodecType::kIsac: { |
Henrik Lundin | f1061c2 | 2017-12-07 10:13:47 +0100 | [diff] [blame] | 240 | return AudioEncoderIsac::MakeAudioEncoder( |
| 241 | GetCodecConfig<AudioEncoderIsac>(), payload_type); |
Henrik Lundin | 1391bd4 | 2017-11-24 09:28:57 +0100 | [diff] [blame] | 242 | } |
| 243 | } |
| 244 | RTC_NOTREACHED(); |
| 245 | return nullptr; |
| 246 | } |
| 247 | |
Karl Wiberg | 2365936 | 2018-11-01 11:13:44 +0100 | [diff] [blame] | 248 | AudioEncoderCngConfig GetCngConfig(int sample_rate_hz) { |
| 249 | AudioEncoderCngConfig cng_config; |
Henrik Lundin | 1391bd4 | 2017-11-24 09:28:57 +0100 | [diff] [blame] | 250 | const auto default_payload_type = [&] { |
| 251 | switch (sample_rate_hz) { |
Yves Gerey | 665174f | 2018-06-19 15:03:05 +0200 | [diff] [blame] | 252 | 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 Lundin | 1391bd4 | 2017-11-24 09:28:57 +0100 | [diff] [blame] | 262 | } |
| 263 | return 0; |
| 264 | }; |
Mirko Bonadei | 14be799 | 2019-06-27 15:59:09 +0200 | [diff] [blame] | 265 | cng_config.payload_type = absl::GetFlag(FLAGS_cng_payload_type) != -1 |
| 266 | ? absl::GetFlag(FLAGS_cng_payload_type) |
Henrik Lundin | 1391bd4 | 2017-11-24 09:28:57 +0100 | [diff] [blame] | 267 | : default_payload_type(); |
| 268 | return cng_config; |
| 269 | } |
| 270 | |
| 271 | int RunRtpEncode(int argc, char* argv[]) { |
Mirko Bonadei | 14be799 | 2019-06-27 15:59:09 +0200 | [diff] [blame] | 272 | std::vector<char*> args = absl::ParseCommandLine(argc, argv); |
Henrik Lundin | 1391bd4 | 2017-11-24 09:28:57 +0100 | [diff] [blame] | 273 | const std::string usage = |
| 274 | "Tool for generating an RTP dump file from audio input.\n" |
Mirko Bonadei | 14be799 | 2019-06-27 15:59:09 +0200 | [diff] [blame] | 275 | "Example usage:\n" |
| 276 | "./rtp_encode input.pcm output.rtp --codec=[codec] " |
Henrik Lundin | 1391bd4 | 2017-11-24 09:28:57 +0100 | [diff] [blame] | 277 | "--frame_len=[frame_len] --bitrate=[bitrate]\n\n"; |
Mirko Bonadei | 14be799 | 2019-06-27 15:59:09 +0200 | [diff] [blame] | 278 | if (!absl::GetFlag(FLAGS_list_codecs) && args.size() != 3) { |
Henrik Lundin | 1391bd4 | 2017-11-24 09:28:57 +0100 | [diff] [blame] | 279 | printf("%s", usage.c_str()); |
Henrik Lundin | 1391bd4 | 2017-11-24 09:28:57 +0100 | [diff] [blame] | 280 | return 1; |
| 281 | } |
| 282 | |
Mirko Bonadei | 14be799 | 2019-06-27 15:59:09 +0200 | [diff] [blame] | 283 | if (absl::GetFlag(FLAGS_list_codecs)) { |
Henrik Lundin | 1391bd4 | 2017-11-24 09:28:57 +0100 | [diff] [blame] | 284 | 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 Bonadei | 14be799 | 2019-06-27 15:59:09 +0200 | [diff] [blame] | 291 | const auto codec_it = CodecList().find(absl::GetFlag(FLAGS_codec)); |
Henrik Lundin | 1391bd4 | 2017-11-24 09:28:57 +0100 | [diff] [blame] | 292 | if (codec_it == CodecList().end()) { |
Mirko Bonadei | 14be799 | 2019-06-27 15:59:09 +0200 | [diff] [blame] | 293 | printf("%s is not a valid codec name.\n", |
| 294 | absl::GetFlag(FLAGS_codec).c_str()); |
Henrik Lundin | 1391bd4 | 2017-11-24 09:28:57 +0100 | [diff] [blame] | 295 | printf("Use argument --list_codecs to see all valid codec names.\n"); |
| 296 | return 1; |
| 297 | } |
| 298 | |
| 299 | // Create the codec. |
Mirko Bonadei | 14be799 | 2019-06-27 15:59:09 +0200 | [diff] [blame] | 300 | const int payload_type = absl::GetFlag(FLAGS_payload_type) == -1 |
Henrik Lundin | 1391bd4 | 2017-11-24 09:28:57 +0100 | [diff] [blame] | 301 | ? codec_it->second.default_payload_type |
Mirko Bonadei | 14be799 | 2019-06-27 15:59:09 +0200 | [diff] [blame] | 302 | : absl::GetFlag(FLAGS_payload_type); |
Henrik Lundin | 1391bd4 | 2017-11-24 09:28:57 +0100 | [diff] [blame] | 303 | std::unique_ptr<AudioEncoder> codec = |
| 304 | CreateEncoder(codec_it->second.type, payload_type); |
| 305 | |
| 306 | // Create an external VAD/CNG encoder if needed. |
Mirko Bonadei | 14be799 | 2019-06-27 15:59:09 +0200 | [diff] [blame] | 307 | if (absl::GetFlag(FLAGS_dtx) && !codec_it->second.internal_dtx) { |
Karl Wiberg | 2365936 | 2018-11-01 11:13:44 +0100 | [diff] [blame] | 308 | AudioEncoderCngConfig cng_config = GetCngConfig(codec->SampleRateHz()); |
Henrik Lundin | 1391bd4 | 2017-11-24 09:28:57 +0100 | [diff] [blame] | 309 | RTC_DCHECK(codec); |
| 310 | cng_config.speech_encoder = std::move(codec); |
Karl Wiberg | 2365936 | 2018-11-01 11:13:44 +0100 | [diff] [blame] | 311 | codec = CreateComfortNoiseEncoder(std::move(cng_config)); |
Henrik Lundin | 1391bd4 | 2017-11-24 09:28:57 +0100 | [diff] [blame] | 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. |
Mirko Bonadei | 14be799 | 2019-06-27 15:59:09 +0200 | [diff] [blame] | 322 | 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 Lundin | 1391bd4 | 2017-11-24 09:28:57 +0100 | [diff] [blame] | 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. |
Mirko Bonadei | 14be799 | 2019-06-27 15:59:09 +0200 | [diff] [blame] | 334 | Packetizer packetizer(out_file, absl::GetFlag(FLAGS_ssrc), timestamp_rate_hz); |
Henrik Lundin | 1391bd4 | 2017-11-24 09:28:57 +0100 | [diff] [blame] | 335 | RTC_DCHECK_EQ(acm->RegisterTransportCallback(&packetizer), 0); |
| 336 | |
| 337 | AudioFrame audio_frame; |
Mirko Bonadei | 14be799 | 2019-06-27 15:59:09 +0200 | [diff] [blame] | 338 | 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 Lundin | 1391bd4 | 2017-11-24 09:28:57 +0100 | [diff] [blame] | 341 | 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 | |
| 356 | int main(int argc, char* argv[]) { |
| 357 | return webrtc::test::RunRtpEncode(argc, argv); |
| 358 | } |