| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. | 2 * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license | 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 | 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 | 6 * tree. An additional intellectual property rights grant can be found |
| 7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
| 8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
| 9 */ | 9 */ |
| 10 | 10 |
| 11 // TODO(hlundin): The functionality in this file should be moved into one or | |
| 12 // several classes. | |
| 13 | |
| 14 #include <assert.h> | |
| 15 #include <errno.h> | 11 #include <errno.h> |
| 12 #include <inttypes.h> |
| 16 #include <limits.h> // For ULONG_MAX returned by strtoul. | 13 #include <limits.h> // For ULONG_MAX returned by strtoul. |
| 17 #include <stdio.h> | 14 #include <stdio.h> |
| 18 #include <stdlib.h> // For strtoul. | 15 #include <stdlib.h> // For strtoul. |
| 19 | 16 |
| 20 #include <algorithm> | 17 #include <algorithm> |
| 21 #include <iostream> | 18 #include <iostream> |
| 22 #include <memory> | 19 #include <memory> |
| 23 #include <limits> | |
| 24 #include <string> | 20 #include <string> |
| 25 | 21 |
| 26 #include "gflags/gflags.h" | 22 #include "gflags/gflags.h" |
| 27 #include "webrtc/base/checks.h" | 23 #include "webrtc/base/checks.h" |
| 28 #include "webrtc/base/safe_conversions.h" | |
| 29 #include "webrtc/modules/audio_coding/codecs/builtin_audio_decoder_factory.h" | |
| 30 #include "webrtc/modules/audio_coding/codecs/pcm16b/pcm16b.h" | |
| 31 #include "webrtc/modules/audio_coding/neteq/include/neteq.h" | 24 #include "webrtc/modules/audio_coding/neteq/include/neteq.h" |
| 25 #include "webrtc/modules/audio_coding/neteq/tools/fake_decode_from_file.h" |
| 32 #include "webrtc/modules/audio_coding/neteq/tools/input_audio_file.h" | 26 #include "webrtc/modules/audio_coding/neteq/tools/input_audio_file.h" |
| 27 #include "webrtc/modules/audio_coding/neteq/tools/neteq_packet_source_input.h" |
| 28 #include "webrtc/modules/audio_coding/neteq/tools/neteq_replacement_input.h" |
| 29 #include "webrtc/modules/audio_coding/neteq/tools/neteq_test.h" |
| 33 #include "webrtc/modules/audio_coding/neteq/tools/output_audio_file.h" | 30 #include "webrtc/modules/audio_coding/neteq/tools/output_audio_file.h" |
| 34 #include "webrtc/modules/audio_coding/neteq/tools/output_wav_file.h" | 31 #include "webrtc/modules/audio_coding/neteq/tools/output_wav_file.h" |
| 35 #include "webrtc/modules/audio_coding/neteq/tools/packet.h" | |
| 36 #include "webrtc/modules/audio_coding/neteq/tools/rtc_event_log_source.h" | |
| 37 #include "webrtc/modules/audio_coding/neteq/tools/rtp_file_source.h" | 32 #include "webrtc/modules/audio_coding/neteq/tools/rtp_file_source.h" |
| 38 #include "webrtc/modules/include/module_common_types.h" | 33 #include "webrtc/modules/include/module_common_types.h" |
| 39 #include "webrtc/system_wrappers/include/trace.h" | |
| 40 #include "webrtc/test/rtp_file_reader.h" | |
| 41 #include "webrtc/test/testsupport/fileutils.h" | 34 #include "webrtc/test/testsupport/fileutils.h" |
| 42 #include "webrtc/typedefs.h" | 35 #include "webrtc/typedefs.h" |
| 43 | 36 |
| 44 namespace webrtc { | 37 namespace webrtc { |
| 45 namespace test { | 38 namespace test { |
| 46 namespace { | 39 namespace { |
| 47 | 40 |
| 48 // Parses the input string for a valid SSRC (at the start of the string). If a | 41 // Parses the input string for a valid SSRC (at the start of the string). If a |
| 49 // valid SSRC is found, it is written to the output variable |ssrc|, and true is | 42 // valid SSRC is found, it is written to the output variable |ssrc|, and true is |
| 50 // returned. Otherwise, false is returned. | 43 // returned. Otherwise, false is returned. |
| (...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 175 return "AVT/DTMF"; | 168 return "AVT/DTMF"; |
| 176 case NetEqDecoder::kDecoderCNGnb: | 169 case NetEqDecoder::kDecoderCNGnb: |
| 177 return "comfort noise (8 kHz)"; | 170 return "comfort noise (8 kHz)"; |
| 178 case NetEqDecoder::kDecoderCNGwb: | 171 case NetEqDecoder::kDecoderCNGwb: |
| 179 return "comfort noise (16 kHz)"; | 172 return "comfort noise (16 kHz)"; |
| 180 case NetEqDecoder::kDecoderCNGswb32kHz: | 173 case NetEqDecoder::kDecoderCNGswb32kHz: |
| 181 return "comfort noise (32 kHz)"; | 174 return "comfort noise (32 kHz)"; |
| 182 case NetEqDecoder::kDecoderCNGswb48kHz: | 175 case NetEqDecoder::kDecoderCNGswb48kHz: |
| 183 return "comfort noise (48 kHz)"; | 176 return "comfort noise (48 kHz)"; |
| 184 default: | 177 default: |
| 185 assert(false); | 178 FATAL(); |
| 186 return "undefined"; | 179 return "undefined"; |
| 187 } | 180 } |
| 188 } | 181 } |
| 189 | 182 |
| 190 void RegisterPayloadType(NetEq* neteq, | |
| 191 NetEqDecoder codec, | |
| 192 const std::string& name, | |
| 193 google::int32 flag) { | |
| 194 if (neteq->RegisterPayloadType(codec, name, static_cast<uint8_t>(flag))) { | |
| 195 std::cerr << "Cannot register payload type " << flag << " as " | |
| 196 << CodecName(codec) << std::endl; | |
| 197 exit(1); | |
| 198 } | |
| 199 } | |
| 200 | |
| 201 // Registers all decoders in |neteq|. | |
| 202 void RegisterPayloadTypes(NetEq* neteq) { | |
| 203 assert(neteq); | |
| 204 RegisterPayloadType(neteq, NetEqDecoder::kDecoderPCMu, "pcmu", FLAGS_pcmu); | |
| 205 RegisterPayloadType(neteq, NetEqDecoder::kDecoderPCMa, "pcma", FLAGS_pcma); | |
| 206 RegisterPayloadType(neteq, NetEqDecoder::kDecoderILBC, "ilbc", FLAGS_ilbc); | |
| 207 RegisterPayloadType(neteq, NetEqDecoder::kDecoderISAC, "isac", FLAGS_isac); | |
| 208 RegisterPayloadType(neteq, NetEqDecoder::kDecoderISACswb, "isac-swb", | |
| 209 FLAGS_isac_swb); | |
| 210 RegisterPayloadType(neteq, NetEqDecoder::kDecoderOpus, "opus", FLAGS_opus); | |
| 211 RegisterPayloadType(neteq, NetEqDecoder::kDecoderPCM16B, "pcm16-nb", | |
| 212 FLAGS_pcm16b); | |
| 213 RegisterPayloadType(neteq, NetEqDecoder::kDecoderPCM16Bwb, "pcm16-wb", | |
| 214 FLAGS_pcm16b_wb); | |
| 215 RegisterPayloadType(neteq, NetEqDecoder::kDecoderPCM16Bswb32kHz, | |
| 216 "pcm16-swb32", FLAGS_pcm16b_swb32); | |
| 217 RegisterPayloadType(neteq, NetEqDecoder::kDecoderPCM16Bswb48kHz, | |
| 218 "pcm16-swb48", FLAGS_pcm16b_swb48); | |
| 219 RegisterPayloadType(neteq, NetEqDecoder::kDecoderG722, "g722", FLAGS_g722); | |
| 220 RegisterPayloadType(neteq, NetEqDecoder::kDecoderAVT, "avt", FLAGS_avt); | |
| 221 RegisterPayloadType(neteq, NetEqDecoder::kDecoderRED, "red", FLAGS_red); | |
| 222 RegisterPayloadType(neteq, NetEqDecoder::kDecoderCNGnb, "cng-nb", | |
| 223 FLAGS_cn_nb); | |
| 224 RegisterPayloadType(neteq, NetEqDecoder::kDecoderCNGwb, "cng-wb", | |
| 225 FLAGS_cn_wb); | |
| 226 RegisterPayloadType(neteq, NetEqDecoder::kDecoderCNGswb32kHz, "cng-swb32", | |
| 227 FLAGS_cn_swb32); | |
| 228 RegisterPayloadType(neteq, NetEqDecoder::kDecoderCNGswb48kHz, "cng-swb48", | |
| 229 FLAGS_cn_swb48); | |
| 230 } | |
| 231 | |
| 232 void PrintCodecMappingEntry(NetEqDecoder codec, google::int32 flag) { | 183 void PrintCodecMappingEntry(NetEqDecoder codec, google::int32 flag) { |
| 233 std::cout << CodecName(codec) << ": " << flag << std::endl; | 184 std::cout << CodecName(codec) << ": " << flag << std::endl; |
| 234 } | 185 } |
| 235 | 186 |
| 236 void PrintCodecMapping() { | 187 void PrintCodecMapping() { |
| 237 PrintCodecMappingEntry(NetEqDecoder::kDecoderPCMu, FLAGS_pcmu); | 188 PrintCodecMappingEntry(NetEqDecoder::kDecoderPCMu, FLAGS_pcmu); |
| 238 PrintCodecMappingEntry(NetEqDecoder::kDecoderPCMa, FLAGS_pcma); | 189 PrintCodecMappingEntry(NetEqDecoder::kDecoderPCMa, FLAGS_pcma); |
| 239 PrintCodecMappingEntry(NetEqDecoder::kDecoderILBC, FLAGS_ilbc); | 190 PrintCodecMappingEntry(NetEqDecoder::kDecoderILBC, FLAGS_ilbc); |
| 240 PrintCodecMappingEntry(NetEqDecoder::kDecoderISAC, FLAGS_isac); | 191 PrintCodecMappingEntry(NetEqDecoder::kDecoderISAC, FLAGS_isac); |
| 241 PrintCodecMappingEntry(NetEqDecoder::kDecoderISACswb, FLAGS_isac_swb); | 192 PrintCodecMappingEntry(NetEqDecoder::kDecoderISACswb, FLAGS_isac_swb); |
| 242 PrintCodecMappingEntry(NetEqDecoder::kDecoderOpus, FLAGS_opus); | 193 PrintCodecMappingEntry(NetEqDecoder::kDecoderOpus, FLAGS_opus); |
| 243 PrintCodecMappingEntry(NetEqDecoder::kDecoderPCM16B, FLAGS_pcm16b); | 194 PrintCodecMappingEntry(NetEqDecoder::kDecoderPCM16B, FLAGS_pcm16b); |
| 244 PrintCodecMappingEntry(NetEqDecoder::kDecoderPCM16Bwb, FLAGS_pcm16b_wb); | 195 PrintCodecMappingEntry(NetEqDecoder::kDecoderPCM16Bwb, FLAGS_pcm16b_wb); |
| 245 PrintCodecMappingEntry(NetEqDecoder::kDecoderPCM16Bswb32kHz, | 196 PrintCodecMappingEntry(NetEqDecoder::kDecoderPCM16Bswb32kHz, |
| 246 FLAGS_pcm16b_swb32); | 197 FLAGS_pcm16b_swb32); |
| 247 PrintCodecMappingEntry(NetEqDecoder::kDecoderPCM16Bswb48kHz, | 198 PrintCodecMappingEntry(NetEqDecoder::kDecoderPCM16Bswb48kHz, |
| 248 FLAGS_pcm16b_swb48); | 199 FLAGS_pcm16b_swb48); |
| 249 PrintCodecMappingEntry(NetEqDecoder::kDecoderG722, FLAGS_g722); | 200 PrintCodecMappingEntry(NetEqDecoder::kDecoderG722, FLAGS_g722); |
| 250 PrintCodecMappingEntry(NetEqDecoder::kDecoderAVT, FLAGS_avt); | 201 PrintCodecMappingEntry(NetEqDecoder::kDecoderAVT, FLAGS_avt); |
| 251 PrintCodecMappingEntry(NetEqDecoder::kDecoderRED, FLAGS_red); | 202 PrintCodecMappingEntry(NetEqDecoder::kDecoderRED, FLAGS_red); |
| 252 PrintCodecMappingEntry(NetEqDecoder::kDecoderCNGnb, FLAGS_cn_nb); | 203 PrintCodecMappingEntry(NetEqDecoder::kDecoderCNGnb, FLAGS_cn_nb); |
| 253 PrintCodecMappingEntry(NetEqDecoder::kDecoderCNGwb, FLAGS_cn_wb); | 204 PrintCodecMappingEntry(NetEqDecoder::kDecoderCNGwb, FLAGS_cn_wb); |
| 254 PrintCodecMappingEntry(NetEqDecoder::kDecoderCNGswb32kHz, FLAGS_cn_swb32); | 205 PrintCodecMappingEntry(NetEqDecoder::kDecoderCNGswb32kHz, FLAGS_cn_swb32); |
| 255 PrintCodecMappingEntry(NetEqDecoder::kDecoderCNGswb48kHz, FLAGS_cn_swb48); | 206 PrintCodecMappingEntry(NetEqDecoder::kDecoderCNGswb48kHz, FLAGS_cn_swb48); |
| 256 } | 207 } |
| 257 | 208 |
| 258 bool IsComfortNoise(uint8_t payload_type) { | |
| 259 return payload_type == FLAGS_cn_nb || payload_type == FLAGS_cn_wb || | |
| 260 payload_type == FLAGS_cn_swb32 || payload_type == FLAGS_cn_swb48; | |
| 261 } | |
| 262 | |
| 263 int CodecSampleRate(uint8_t payload_type) { | 209 int CodecSampleRate(uint8_t payload_type) { |
| 264 if (payload_type == FLAGS_pcmu || payload_type == FLAGS_pcma || | 210 if (payload_type == FLAGS_pcmu || payload_type == FLAGS_pcma || |
| 265 payload_type == FLAGS_ilbc || payload_type == FLAGS_pcm16b || | 211 payload_type == FLAGS_ilbc || payload_type == FLAGS_pcm16b || |
| 266 payload_type == FLAGS_cn_nb) | 212 payload_type == FLAGS_cn_nb) |
| 267 return 8000; | 213 return 8000; |
| 268 if (payload_type == FLAGS_isac || payload_type == FLAGS_pcm16b_wb || | 214 if (payload_type == FLAGS_isac || payload_type == FLAGS_pcm16b_wb || |
| 269 payload_type == FLAGS_g722 || payload_type == FLAGS_cn_wb) | 215 payload_type == FLAGS_g722 || payload_type == FLAGS_cn_wb) |
| 270 return 16000; | 216 return 16000; |
| 271 if (payload_type == FLAGS_isac_swb || payload_type == FLAGS_pcm16b_swb32 || | 217 if (payload_type == FLAGS_isac_swb || payload_type == FLAGS_pcm16b_swb32 || |
| 272 payload_type == FLAGS_cn_swb32) | 218 payload_type == FLAGS_cn_swb32) |
| 273 return 32000; | 219 return 32000; |
| 274 if (payload_type == FLAGS_opus || payload_type == FLAGS_pcm16b_swb48 || | 220 if (payload_type == FLAGS_opus || payload_type == FLAGS_pcm16b_swb48 || |
| 275 payload_type == FLAGS_cn_swb48) | 221 payload_type == FLAGS_cn_swb48) |
| 276 return 48000; | 222 return 48000; |
| 277 if (payload_type == FLAGS_avt || payload_type == FLAGS_red) | 223 if (payload_type == FLAGS_avt || payload_type == FLAGS_red) |
| 278 return 0; | 224 return 0; |
| 279 return -1; | 225 return -1; |
| 280 } | 226 } |
| 281 | 227 |
| 282 int CodecTimestampRate(uint8_t payload_type) { | 228 // Class to let through only the packets with a given SSRC. Should be used as an |
| 283 return (payload_type == FLAGS_g722) ? 8000 : CodecSampleRate(payload_type); | 229 // outer layer on another NetEqInput object. |
| 284 } | 230 class FilterSsrcInput : public NetEqInput { |
| 231 public: |
| 232 FilterSsrcInput(std::unique_ptr<NetEqInput> source, uint32_t ssrc) |
| 233 : source_(std::move(source)), ssrc_(ssrc) { |
| 234 FindNextWithCorrectSsrc(); |
| 235 } |
| 285 | 236 |
| 286 size_t ReplacePayload(InputAudioFile* replacement_audio_file, | 237 // All methods but PopPacket() simply relay to the |source_| object. |
| 287 std::unique_ptr<int16_t[]>* replacement_audio, | 238 rtc::Optional<int64_t> NextPacketTime() const override { |
| 288 std::unique_ptr<uint8_t[]>* payload, | 239 return source_->NextPacketTime(); |
| 289 size_t* payload_mem_size_bytes, | 240 } |
| 290 size_t* frame_size_samples, | 241 rtc::Optional<int64_t> NextOutputEventTime() const override { |
| 291 WebRtcRTPHeader* rtp_header, | 242 return source_->NextOutputEventTime(); |
| 292 const Packet* next_packet) { | 243 } |
| 293 size_t payload_len = 0; | 244 |
| 294 // Check for CNG. | 245 // Returns the next packet, and throws away upcoming packets that do not match |
| 295 if (IsComfortNoise(rtp_header->header.payloadType)) { | 246 // the desired SSRC. |
| 296 // If CNG, simply insert a zero-energy one-byte payload. | 247 std::unique_ptr<PacketData> PopPacket() override { |
| 297 if (*payload_mem_size_bytes < 1) { | 248 std::unique_ptr<PacketData> packet_to_return = source_->PopPacket(); |
| 298 (*payload).reset(new uint8_t[1]); | 249 RTC_DCHECK(!packet_to_return || |
| 299 *payload_mem_size_bytes = 1; | 250 packet_to_return->header.header.ssrc == ssrc_); |
| 300 } | 251 // Pre-fetch the next packet with correct SSRC. Hence, |source_| will always |
| 301 (*payload)[0] = 127; // Max attenuation of CNG. | 252 // be have a valid packet (or empty if no more packets are available) when |
| 302 payload_len = 1; | 253 // this method returns. |
| 303 } else { | 254 FindNextWithCorrectSsrc(); |
| 304 assert(next_packet->virtual_payload_length_bytes() > 0); | 255 return packet_to_return; |
| 305 // Check if payload length has changed. | 256 } |
| 306 if (next_packet->header().sequenceNumber == | 257 |
| 307 rtp_header->header.sequenceNumber + 1) { | 258 void AdvanceOutputEvent() override { source_->AdvanceOutputEvent(); } |
| 308 if (*frame_size_samples != | 259 |
| 309 next_packet->header().timestamp - rtp_header->header.timestamp) { | 260 bool ended() const override { return source_->ended(); } |
| 310 *frame_size_samples = | 261 |
| 311 next_packet->header().timestamp - rtp_header->header.timestamp; | 262 rtc::Optional<RTPHeader> NextHeader() const override { |
| 312 (*replacement_audio).reset( | 263 return source_->NextHeader(); |
| 313 new int16_t[*frame_size_samples]); | 264 } |
| 314 *payload_mem_size_bytes = 2 * *frame_size_samples; | 265 |
| 315 (*payload).reset(new uint8_t[*payload_mem_size_bytes]); | 266 private: |
| 316 } | 267 void FindNextWithCorrectSsrc() { |
| 317 } | 268 while (source_->NextHeader() && source_->NextHeader()->ssrc != ssrc_) { |
| 318 // Get new speech. | 269 source_->PopPacket(); |
| 319 assert((*replacement_audio).get()); | |
| 320 if (CodecTimestampRate(rtp_header->header.payloadType) != | |
| 321 CodecSampleRate(rtp_header->header.payloadType) || | |
| 322 rtp_header->header.payloadType == FLAGS_red || | |
| 323 rtp_header->header.payloadType == FLAGS_avt) { | |
| 324 // Some codecs have different sample and timestamp rates. And neither | |
| 325 // RED nor DTMF is supported for replacement. | |
| 326 std::cerr << "Codec not supported for audio replacement." << | |
| 327 std::endl; | |
| 328 Trace::ReturnTrace(); | |
| 329 exit(1); | |
| 330 } | |
| 331 assert(*frame_size_samples > 0); | |
| 332 if (!replacement_audio_file->Read(*frame_size_samples, | |
| 333 (*replacement_audio).get())) { | |
| 334 std::cerr << "Could not read replacement audio file." << std::endl; | |
| 335 Trace::ReturnTrace(); | |
| 336 exit(1); | |
| 337 } | |
| 338 // Encode it as PCM16. | |
| 339 assert((*payload).get()); | |
| 340 payload_len = WebRtcPcm16b_Encode((*replacement_audio).get(), | |
| 341 *frame_size_samples, | |
| 342 (*payload).get()); | |
| 343 assert(payload_len == 2 * *frame_size_samples); | |
| 344 // Change payload type to PCM16. | |
| 345 switch (CodecSampleRate(rtp_header->header.payloadType)) { | |
| 346 case 8000: | |
| 347 rtp_header->header.payloadType = static_cast<uint8_t>(FLAGS_pcm16b); | |
| 348 break; | |
| 349 case 16000: | |
| 350 rtp_header->header.payloadType = static_cast<uint8_t>(FLAGS_pcm16b_wb); | |
| 351 break; | |
| 352 case 32000: | |
| 353 rtp_header->header.payloadType = | |
| 354 static_cast<uint8_t>(FLAGS_pcm16b_swb32); | |
| 355 break; | |
| 356 case 48000: | |
| 357 rtp_header->header.payloadType = | |
| 358 static_cast<uint8_t>(FLAGS_pcm16b_swb48); | |
| 359 break; | |
| 360 default: | |
| 361 std::cerr << "Payload type " << | |
| 362 static_cast<int>(rtp_header->header.payloadType) << | |
| 363 " not supported or unknown." << std::endl; | |
| 364 Trace::ReturnTrace(); | |
| 365 exit(1); | |
| 366 } | 270 } |
| 367 } | 271 } |
| 368 return payload_len; | 272 |
| 369 } | 273 std::unique_ptr<NetEqInput> source_; |
| 274 uint32_t ssrc_; |
| 275 }; |
| 370 | 276 |
| 371 int RunTest(int argc, char* argv[]) { | 277 int RunTest(int argc, char* argv[]) { |
| 372 static const int kOutputBlockSizeMs = 10; | |
| 373 | |
| 374 std::string program_name = argv[0]; | 278 std::string program_name = argv[0]; |
| 375 std::string usage = "Tool for decoding an RTP dump file using NetEq.\n" | 279 std::string usage = "Tool for decoding an RTP dump file using NetEq.\n" |
| 376 "Run " + program_name + " --helpshort for usage.\n" | 280 "Run " + program_name + " --helpshort for usage.\n" |
| 377 "Example usage:\n" + program_name + | 281 "Example usage:\n" + program_name + |
| 378 " input.rtp output.{pcm, wav}\n"; | 282 " input.rtp output.{pcm, wav}\n"; |
| 379 google::SetUsageMessage(usage); | 283 google::SetUsageMessage(usage); |
| 380 google::ParseCommandLineFlags(&argc, &argv, true); | 284 google::ParseCommandLineFlags(&argc, &argv, true); |
| 381 | 285 |
| 382 if (FLAGS_codec_map) { | 286 if (FLAGS_codec_map) { |
| 383 PrintCodecMapping(); | 287 PrintCodecMapping(); |
| 384 } | 288 } |
| 385 | 289 |
| 386 if (argc != 3) { | 290 if (argc != 3) { |
| 387 if (FLAGS_codec_map) { | 291 if (FLAGS_codec_map) { |
| 388 // We have already printed the codec map. Just end the program. | 292 // We have already printed the codec map. Just end the program. |
| 389 return 0; | 293 return 0; |
| 390 } | 294 } |
| 391 // Print usage information. | 295 // Print usage information. |
| 392 std::cout << google::ProgramUsage(); | 296 std::cout << google::ProgramUsage(); |
| 393 return 0; | 297 return 0; |
| 394 } | 298 } |
| 395 | 299 |
| 396 printf("Input file: %s\n", argv[1]); | 300 const std::string input_file_name = argv[1]; |
| 397 | 301 std::unique_ptr<NetEqInput> input; |
| 398 bool is_rtp_dump = false; | 302 if (RtpFileSource::ValidRtpDump(input_file_name) || |
| 399 std::unique_ptr<PacketSource> file_source; | 303 RtpFileSource::ValidPcap(input_file_name)) { |
| 400 RtcEventLogSource* event_log_source = nullptr; | 304 input.reset(new NetEqRtpDumpInput(input_file_name)); |
| 401 if (RtpFileSource::ValidRtpDump(argv[1]) || | |
| 402 RtpFileSource::ValidPcap(argv[1])) { | |
| 403 is_rtp_dump = true; | |
| 404 file_source.reset(RtpFileSource::Create(argv[1])); | |
| 405 } else { | 305 } else { |
| 406 event_log_source = RtcEventLogSource::Create(argv[1]); | 306 input.reset(new NetEqEventLogInput(input_file_name)); |
| 407 file_source.reset(event_log_source); | |
| 408 } | 307 } |
| 409 | 308 |
| 410 assert(file_source.get()); | 309 std::cout << "Input file: " << input_file_name << std::endl; |
| 310 RTC_CHECK(input) << "Cannot open input file"; |
| 311 RTC_CHECK(!input->ended()) << "Input file is empty"; |
| 411 | 312 |
| 412 // Check if an SSRC value was provided. | 313 // Check if an SSRC value was provided. |
| 413 if (!FLAGS_ssrc.empty()) { | 314 if (!FLAGS_ssrc.empty()) { |
| 414 uint32_t ssrc; | 315 uint32_t ssrc; |
| 415 RTC_CHECK(ParseSsrc(FLAGS_ssrc, &ssrc)) << "Flag verification has failed."; | 316 RTC_CHECK(ParseSsrc(FLAGS_ssrc, &ssrc)) << "Flag verification has failed."; |
| 416 file_source->SelectSsrc(ssrc); | 317 input.reset(new FilterSsrcInput(std::move(input), ssrc)); |
| 417 } | |
| 418 | |
| 419 // Check if a replacement audio file was provided, and if so, open it. | |
| 420 bool replace_payload = false; | |
| 421 std::unique_ptr<InputAudioFile> replacement_audio_file; | |
| 422 if (!FLAGS_replacement_audio_file.empty()) { | |
| 423 replacement_audio_file.reset( | |
| 424 new InputAudioFile(FLAGS_replacement_audio_file)); | |
| 425 replace_payload = true; | |
| 426 } | |
| 427 | |
| 428 // Read first packet. | |
| 429 std::unique_ptr<Packet> packet(file_source->NextPacket()); | |
| 430 if (!packet) { | |
| 431 printf( | |
| 432 "Warning: input file is empty, or the filters did not match any " | |
| 433 "packets\n"); | |
| 434 Trace::ReturnTrace(); | |
| 435 return 0; | |
| 436 } | |
| 437 if (packet->payload_length_bytes() == 0 && !replace_payload) { | |
| 438 std::cerr << "Warning: input file contains header-only packets, but no " | |
| 439 << "replacement file is specified." << std::endl; | |
| 440 Trace::ReturnTrace(); | |
| 441 return -1; | |
| 442 } | 318 } |
| 443 | 319 |
| 444 // Check the sample rate. | 320 // Check the sample rate. |
| 445 int sample_rate_hz = CodecSampleRate(packet->header().payloadType); | 321 rtc::Optional<RTPHeader> first_rtp_header = input->NextHeader(); |
| 446 if (sample_rate_hz <= 0) { | 322 RTC_CHECK(first_rtp_header); |
| 447 printf("Warning: Invalid sample rate from RTP packet.\n"); | 323 const int sample_rate_hz = CodecSampleRate(first_rtp_header->payloadType); |
| 448 Trace::ReturnTrace(); | 324 RTC_CHECK_GT(sample_rate_hz, 0); |
| 449 return 0; | |
| 450 } | |
| 451 | 325 |
| 452 // Open the output file now that we know the sample rate. (Rate is only needed | 326 // Open the output file now that we know the sample rate. (Rate is only needed |
| 453 // for wav files.) | 327 // for wav files.) |
| 454 // Check output file type. | 328 const std::string output_file_name = argv[2]; |
| 455 std::string output_file_name = argv[2]; | |
| 456 std::unique_ptr<AudioSink> output; | 329 std::unique_ptr<AudioSink> output; |
| 457 if (output_file_name.size() >= 4 && | 330 if (output_file_name.size() >= 4 && |
| 458 output_file_name.substr(output_file_name.size() - 4) == ".wav") { | 331 output_file_name.substr(output_file_name.size() - 4) == ".wav") { |
| 459 // Open a wav file. | 332 // Open a wav file. |
| 460 output.reset(new OutputWavFile(output_file_name, sample_rate_hz)); | 333 output.reset(new OutputWavFile(output_file_name, sample_rate_hz)); |
| 461 } else { | 334 } else { |
| 462 // Open a pcm file. | 335 // Open a pcm file. |
| 463 output.reset(new OutputAudioFile(output_file_name)); | 336 output.reset(new OutputAudioFile(output_file_name)); |
| 464 } | 337 } |
| 465 | 338 |
| 466 std::cout << "Output file: " << argv[2] << std::endl; | 339 std::cout << "Output file: " << output_file_name << std::endl; |
| 467 | 340 |
| 468 // Enable tracing. | 341 NetEqTest::DecoderMap codecs = { |
| 469 Trace::CreateTrace(); | 342 {FLAGS_pcmu, std::make_pair(NetEqDecoder::kDecoderPCMu, "pcmu")}, |
| 470 Trace::SetTraceFile((OutputPath() + "neteq_trace.txt").c_str()); | 343 {FLAGS_pcma, std::make_pair(NetEqDecoder::kDecoderPCMa, "pcma")}, |
| 471 Trace::set_level_filter(kTraceAll); | 344 {FLAGS_ilbc, std::make_pair(NetEqDecoder::kDecoderILBC, "ilbc")}, |
| 345 {FLAGS_isac, std::make_pair(NetEqDecoder::kDecoderISAC, "isac")}, |
| 346 {FLAGS_isac_swb, |
| 347 std::make_pair(NetEqDecoder::kDecoderISACswb, "isac-swb")}, |
| 348 {FLAGS_opus, std::make_pair(NetEqDecoder::kDecoderOpus, "opus")}, |
| 349 {FLAGS_pcm16b, std::make_pair(NetEqDecoder::kDecoderPCM16B, "pcm16-nb")}, |
| 350 {FLAGS_pcm16b_wb, |
| 351 std::make_pair(NetEqDecoder::kDecoderPCM16Bwb, "pcm16-wb")}, |
| 352 {FLAGS_pcm16b_swb32, |
| 353 std::make_pair(NetEqDecoder::kDecoderPCM16Bswb32kHz, "pcm16-swb32")}, |
| 354 {FLAGS_pcm16b_swb48, |
| 355 std::make_pair(NetEqDecoder::kDecoderPCM16Bswb48kHz, "pcm16-swb48")}, |
| 356 {FLAGS_g722, std::make_pair(NetEqDecoder::kDecoderG722, "g722")}, |
| 357 {FLAGS_avt, std::make_pair(NetEqDecoder::kDecoderAVT, "avt")}, |
| 358 {FLAGS_red, std::make_pair(NetEqDecoder::kDecoderRED, "red")}, |
| 359 {FLAGS_cn_nb, std::make_pair(NetEqDecoder::kDecoderCNGnb, "cng-nb")}, |
| 360 {FLAGS_cn_wb, std::make_pair(NetEqDecoder::kDecoderCNGwb, "cng-wb")}, |
| 361 {FLAGS_cn_swb32, |
| 362 std::make_pair(NetEqDecoder::kDecoderCNGswb32kHz, "cng-swb32")}, |
| 363 {FLAGS_cn_swb48, |
| 364 std::make_pair(NetEqDecoder::kDecoderCNGswb48kHz, "cng-swb48")}}; |
| 472 | 365 |
| 473 // Initialize NetEq instance. | 366 // Check if a replacement audio file was provided. |
| 367 std::unique_ptr<AudioDecoder> replacement_decoder; |
| 368 NetEqTest::ExtDecoderMap ext_codecs; |
| 369 if (!FLAGS_replacement_audio_file.empty()) { |
| 370 // Find largest unused payload type. |
| 371 int replacement_pt = 127; |
| 372 while (!(codecs.find(replacement_pt) == codecs.end() && |
| 373 ext_codecs.find(replacement_pt) == ext_codecs.end())) { |
| 374 --replacement_pt; |
| 375 RTC_CHECK_GE(replacement_pt, 0); |
| 376 } |
| 377 |
| 378 auto std_set_int32_to_uint8 = [](const std::set<int32_t>& a) { |
| 379 std::set<uint8_t> b; |
| 380 for (auto& x : a) { |
| 381 b.insert(static_cast<uint8_t>(x)); |
| 382 } |
| 383 return b; |
| 384 }; |
| 385 |
| 386 std::set<uint8_t> cn_types = std_set_int32_to_uint8( |
| 387 {FLAGS_cn_nb, FLAGS_cn_wb, FLAGS_cn_swb32, FLAGS_cn_swb48}); |
| 388 std::set<uint8_t> forbidden_types = |
| 389 std_set_int32_to_uint8({FLAGS_g722, FLAGS_red, FLAGS_avt}); |
| 390 input.reset(new NetEqReplacementInput(std::move(input), replacement_pt, |
| 391 cn_types, forbidden_types)); |
| 392 |
| 393 replacement_decoder.reset(new FakeDecodeFromFile( |
| 394 std::unique_ptr<InputAudioFile>( |
| 395 new InputAudioFile(FLAGS_replacement_audio_file)), |
| 396 48000, false)); |
| 397 NetEqTest::ExternalDecoderInfo ext_dec_info = { |
| 398 replacement_decoder.get(), NetEqDecoder::kDecoderArbitrary, |
| 399 "replacement codec"}; |
| 400 ext_codecs[replacement_pt] = ext_dec_info; |
| 401 } |
| 402 |
| 403 DefaultNetEqTestErrorCallback error_cb; |
| 474 NetEq::Config config; | 404 NetEq::Config config; |
| 475 config.sample_rate_hz = sample_rate_hz; | 405 config.sample_rate_hz = sample_rate_hz; |
| 476 NetEq* neteq = | 406 NetEqTest test(config, codecs, ext_codecs, std::move(input), |
| 477 NetEq::Create(config, CreateBuiltinAudioDecoderFactory()); | 407 std::move(output), &error_cb); |
| 478 RegisterPayloadTypes(neteq); | |
| 479 | 408 |
| 409 int64_t test_duration_ms = test.Run(); |
| 410 NetEqNetworkStatistics stats = test.SimulationStats(); |
| 480 | 411 |
| 481 // Set up variables for audio replacement if needed. | 412 printf("Simulation statistics:\n"); |
| 482 std::unique_ptr<Packet> next_packet; | 413 printf(" output duration: %" PRId64 " ms\n", test_duration_ms); |
| 483 bool next_packet_available = false; | 414 printf(" packet_loss_rate: %f %%\n", |
| 484 size_t input_frame_size_timestamps = 0; | 415 100.0 * stats.packet_loss_rate / 16384.0); |
| 485 std::unique_ptr<int16_t[]> replacement_audio; | 416 printf(" packet_discard_rate: %f %%\n", |
| 486 std::unique_ptr<uint8_t[]> payload; | 417 100.0 * stats.packet_discard_rate / 16384.0); |
| 487 size_t payload_mem_size_bytes = 0; | 418 printf(" expand_rate: %f %%\n", 100.0 * stats.expand_rate / 16384.0); |
| 488 if (replace_payload) { | 419 printf(" speech_expand_rate: %f %%\n", |
| 489 // Initially assume that the frame size is 30 ms at the initial sample rate. | 420 100.0 * stats.speech_expand_rate / 16384.0); |
| 490 // This value will be replaced with the correct one as soon as two | 421 printf(" preemptive_rate: %f %%\n", 100.0 * stats.preemptive_rate / 16384.0); |
| 491 // consecutive packets are found. | 422 printf(" accelerate_rate: %f %%\n", 100.0 * stats.accelerate_rate / 16384.0); |
| 492 input_frame_size_timestamps = 30 * sample_rate_hz / 1000; | 423 printf(" secondary_decoded_rate: %f %%\n", |
| 493 replacement_audio.reset(new int16_t[input_frame_size_timestamps]); | 424 100.0 * stats.secondary_decoded_rate / 16384.0); |
| 494 payload_mem_size_bytes = 2 * input_frame_size_timestamps; | 425 printf(" clockdrift_ppm: %d ppm\n", stats.clockdrift_ppm); |
| 495 payload.reset(new uint8_t[payload_mem_size_bytes]); | 426 printf(" mean_waiting_time_ms: %d ms\n", stats.mean_waiting_time_ms); |
| 496 next_packet = file_source->NextPacket(); | 427 printf(" median_waiting_time_ms: %d ms\n", stats.median_waiting_time_ms); |
| 497 assert(next_packet); | 428 printf(" min_waiting_time_ms: %d ms\n", stats.min_waiting_time_ms); |
| 498 next_packet_available = true; | 429 printf(" max_waiting_time_ms: %d ms\n", stats.max_waiting_time_ms); |
| 499 } | |
| 500 | 430 |
| 501 // This is the main simulation loop. | |
| 502 // Set the simulation clock to start immediately with the first packet. | |
| 503 int64_t start_time_ms = rtc::checked_cast<int64_t>(packet->time_ms()); | |
| 504 int64_t time_now_ms = start_time_ms; | |
| 505 int64_t next_input_time_ms = time_now_ms; | |
| 506 int64_t next_output_time_ms = time_now_ms; | |
| 507 if (time_now_ms % kOutputBlockSizeMs != 0) { | |
| 508 // Make sure that next_output_time_ms is rounded up to the next multiple | |
| 509 // of kOutputBlockSizeMs. (Legacy bit-exactness.) | |
| 510 next_output_time_ms += | |
| 511 kOutputBlockSizeMs - time_now_ms % kOutputBlockSizeMs; | |
| 512 } | |
| 513 | |
| 514 bool packet_available = true; | |
| 515 bool output_event_available = true; | |
| 516 if (!is_rtp_dump) { | |
| 517 next_output_time_ms = event_log_source->NextAudioOutputEventMs(); | |
| 518 if (next_output_time_ms == std::numeric_limits<int64_t>::max()) | |
| 519 output_event_available = false; | |
| 520 start_time_ms = time_now_ms = | |
| 521 std::min(next_input_time_ms, next_output_time_ms); | |
| 522 } | |
| 523 while (packet_available || output_event_available) { | |
| 524 // Advance time to next event. | |
| 525 time_now_ms = std::min(next_input_time_ms, next_output_time_ms); | |
| 526 // Check if it is time to insert packet. | |
| 527 while (time_now_ms >= next_input_time_ms && packet_available) { | |
| 528 assert(packet->virtual_payload_length_bytes() > 0); | |
| 529 // Parse RTP header. | |
| 530 WebRtcRTPHeader rtp_header; | |
| 531 packet->ConvertHeader(&rtp_header); | |
| 532 const uint8_t* payload_ptr = packet->payload(); | |
| 533 size_t payload_len = packet->payload_length_bytes(); | |
| 534 if (replace_payload) { | |
| 535 payload_len = ReplacePayload(replacement_audio_file.get(), | |
| 536 &replacement_audio, | |
| 537 &payload, | |
| 538 &payload_mem_size_bytes, | |
| 539 &input_frame_size_timestamps, | |
| 540 &rtp_header, | |
| 541 next_packet.get()); | |
| 542 payload_ptr = payload.get(); | |
| 543 } | |
| 544 int error = neteq->InsertPacket( | |
| 545 rtp_header, rtc::ArrayView<const uint8_t>(payload_ptr, payload_len), | |
| 546 static_cast<uint32_t>(packet->time_ms() * sample_rate_hz / 1000)); | |
| 547 if (error != NetEq::kOK) { | |
| 548 if (neteq->LastError() == NetEq::kUnknownRtpPayloadType) { | |
| 549 std::cerr << "RTP Payload type " | |
| 550 << static_cast<int>(rtp_header.header.payloadType) | |
| 551 << " is unknown." << std::endl; | |
| 552 std::cerr << "Use --codec_map to view default mapping." << std::endl; | |
| 553 std::cerr << "Use --helpshort for information on how to make custom " | |
| 554 "mappings." << std::endl; | |
| 555 } else { | |
| 556 std::cerr << "InsertPacket returned error code " << neteq->LastError() | |
| 557 << std::endl; | |
| 558 std::cerr << "Header data:" << std::endl; | |
| 559 std::cerr << " PT = " | |
| 560 << static_cast<int>(rtp_header.header.payloadType) | |
| 561 << std::endl; | |
| 562 std::cerr << " SN = " << rtp_header.header.sequenceNumber | |
| 563 << std::endl; | |
| 564 std::cerr << " TS = " << rtp_header.header.timestamp << std::endl; | |
| 565 } | |
| 566 } | |
| 567 | |
| 568 // Get next packet from file. | |
| 569 std::unique_ptr<Packet> temp_packet = file_source->NextPacket(); | |
| 570 if (temp_packet) { | |
| 571 packet = std::move(temp_packet); | |
| 572 if (replace_payload) { | |
| 573 // At this point |packet| contains the packet *after* |next_packet|. | |
| 574 // Swap Packet objects between |packet| and |next_packet|. | |
| 575 packet.swap(next_packet); | |
| 576 // Swap the status indicators unless they're already the same. | |
| 577 if (packet_available != next_packet_available) { | |
| 578 packet_available = !packet_available; | |
| 579 next_packet_available = !next_packet_available; | |
| 580 } | |
| 581 } | |
| 582 next_input_time_ms = rtc::checked_cast<int64_t>(packet->time_ms()); | |
| 583 } else { | |
| 584 // Set next input time to the maximum value of int64_t to prevent the | |
| 585 // time_now_ms from becoming stuck at the final value. | |
| 586 next_input_time_ms = std::numeric_limits<int64_t>::max(); | |
| 587 packet_available = false; | |
| 588 } | |
| 589 RTC_DCHECK(!temp_packet); // Must have transferred to another variable. | |
| 590 } | |
| 591 | |
| 592 // Check if it is time to get output audio. | |
| 593 while (time_now_ms >= next_output_time_ms && output_event_available) { | |
| 594 AudioFrame out_frame; | |
| 595 bool muted; | |
| 596 int error = neteq->GetAudio(&out_frame, &muted); | |
| 597 RTC_CHECK(!muted); | |
| 598 if (error != NetEq::kOK) { | |
| 599 std::cerr << "GetAudio returned error code " << | |
| 600 neteq->LastError() << std::endl; | |
| 601 } else { | |
| 602 sample_rate_hz = out_frame.sample_rate_hz_; | |
| 603 } | |
| 604 | |
| 605 // Write to file. | |
| 606 // TODO(hlundin): Make writing to file optional. | |
| 607 if (!output->WriteArray(out_frame.data_, out_frame.samples_per_channel_ * | |
| 608 out_frame.num_channels_)) { | |
| 609 std::cerr << "Error while writing to file" << std::endl; | |
| 610 Trace::ReturnTrace(); | |
| 611 exit(1); | |
| 612 } | |
| 613 if (is_rtp_dump) { | |
| 614 next_output_time_ms += kOutputBlockSizeMs; | |
| 615 if (!packet_available) | |
| 616 output_event_available = false; | |
| 617 } else { | |
| 618 next_output_time_ms = event_log_source->NextAudioOutputEventMs(); | |
| 619 if (next_output_time_ms == std::numeric_limits<int64_t>::max()) | |
| 620 output_event_available = false; | |
| 621 } | |
| 622 } | |
| 623 } | |
| 624 printf("Simulation done\n"); | |
| 625 printf("Produced %i ms of audio\n", | |
| 626 static_cast<int>(time_now_ms - start_time_ms)); | |
| 627 | |
| 628 delete neteq; | |
| 629 Trace::ReturnTrace(); | |
| 630 return 0; | 431 return 0; |
| 631 } | 432 } |
| 632 | 433 |
| 633 } // namespace | 434 } // namespace |
| 634 } // namespace test | 435 } // namespace test |
| 635 } // namespace webrtc | 436 } // namespace webrtc |
| 636 | 437 |
| 637 int main(int argc, char* argv[]) { | 438 int main(int argc, char* argv[]) { |
| 638 webrtc::test::RunTest(argc, argv); | 439 webrtc::test::RunTest(argc, argv); |
| 639 } | 440 } |
| OLD | NEW |