| /* |
| * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. |
| * |
| * Use of this source code is governed by a BSD-style license |
| * that can be found in the LICENSE file in the root of the source |
| * tree. An additional intellectual property rights grant can be found |
| * in the file PATENTS. All contributing project authors may |
| * be found in the AUTHORS file in the root of the source tree. |
| */ |
| |
| // TODO(hlundin): The functionality in this file should be moved into one or |
| // several classes. |
| |
| #include <assert.h> |
| #include <errno.h> |
| #include <limits.h> // For ULONG_MAX returned by strtoul. |
| #include <stdio.h> |
| #include <stdlib.h> // For strtoul. |
| |
| #include <algorithm> |
| #include <iostream> |
| #include <string> |
| |
| #include "google/gflags.h" |
| #include "webrtc/base/checks.h" |
| #include "webrtc/modules/audio_coding/codecs/pcm16b/include/pcm16b.h" |
| #include "webrtc/modules/audio_coding/neteq/interface/neteq.h" |
| #include "webrtc/modules/audio_coding/neteq/tools/input_audio_file.h" |
| #include "webrtc/modules/audio_coding/neteq/tools/packet.h" |
| #include "webrtc/modules/audio_coding/neteq/tools/rtp_file_source.h" |
| #include "webrtc/modules/interface/module_common_types.h" |
| #include "webrtc/system_wrappers/interface/scoped_ptr.h" |
| #include "webrtc/system_wrappers/interface/trace.h" |
| #include "webrtc/test/testsupport/fileutils.h" |
| #include "webrtc/typedefs.h" |
| |
| using webrtc::NetEq; |
| using webrtc::WebRtcRTPHeader; |
| |
| namespace { |
| // Parses the input string for a valid SSRC (at the start of the string). If a |
| // valid SSRC is found, it is written to the output variable |ssrc|, and true is |
| // returned. Otherwise, false is returned. |
| bool ParseSsrc(const std::string& str, uint32_t* ssrc) { |
| if (str.empty()) |
| return true; |
| int base = 10; |
| // Look for "0x" or "0X" at the start and change base to 16 if found. |
| if ((str.compare(0, 2, "0x") == 0) || (str.compare(0, 2, "0X") == 0)) |
| base = 16; |
| errno = 0; |
| char* end_ptr; |
| unsigned long value = strtoul(str.c_str(), &end_ptr, base); |
| if (value == ULONG_MAX && errno == ERANGE) |
| return false; // Value out of range for unsigned long. |
| if (sizeof(unsigned long) > sizeof(uint32_t) && value > 0xFFFFFFFF) |
| return false; // Value out of range for uint32_t. |
| if (end_ptr - str.c_str() < static_cast<ptrdiff_t>(str.length())) |
| return false; // Part of the string was not parsed. |
| *ssrc = static_cast<uint32_t>(value); |
| return true; |
| } |
| |
| } // namespace |
| |
| // Flag validators. |
| static bool ValidatePayloadType(const char* flagname, int32_t value) { |
| if (value >= 0 && value <= 127) // Value is ok. |
| return true; |
| printf("Invalid value for --%s: %d\n", flagname, static_cast<int>(value)); |
| return false; |
| } |
| |
| static bool ValidateSsrcValue(const char* flagname, const std::string& str) { |
| uint32_t dummy_ssrc; |
| return ParseSsrc(str, &dummy_ssrc); |
| } |
| |
| // Define command line flags. |
| DEFINE_int32(pcmu, 0, "RTP payload type for PCM-u"); |
| static const bool pcmu_dummy = |
| google::RegisterFlagValidator(&FLAGS_pcmu, &ValidatePayloadType); |
| DEFINE_int32(pcma, 8, "RTP payload type for PCM-a"); |
| static const bool pcma_dummy = |
| google::RegisterFlagValidator(&FLAGS_pcma, &ValidatePayloadType); |
| DEFINE_int32(ilbc, 102, "RTP payload type for iLBC"); |
| static const bool ilbc_dummy = |
| google::RegisterFlagValidator(&FLAGS_ilbc, &ValidatePayloadType); |
| DEFINE_int32(isac, 103, "RTP payload type for iSAC"); |
| static const bool isac_dummy = |
| google::RegisterFlagValidator(&FLAGS_isac, &ValidatePayloadType); |
| DEFINE_int32(isac_swb, 104, "RTP payload type for iSAC-swb (32 kHz)"); |
| static const bool isac_swb_dummy = |
| google::RegisterFlagValidator(&FLAGS_isac_swb, &ValidatePayloadType); |
| DEFINE_int32(opus, 111, "RTP payload type for Opus"); |
| static const bool opus_dummy = |
| google::RegisterFlagValidator(&FLAGS_opus, &ValidatePayloadType); |
| DEFINE_int32(pcm16b, 93, "RTP payload type for PCM16b-nb (8 kHz)"); |
| static const bool pcm16b_dummy = |
| google::RegisterFlagValidator(&FLAGS_pcm16b, &ValidatePayloadType); |
| DEFINE_int32(pcm16b_wb, 94, "RTP payload type for PCM16b-wb (16 kHz)"); |
| static const bool pcm16b_wb_dummy = |
| google::RegisterFlagValidator(&FLAGS_pcm16b_wb, &ValidatePayloadType); |
| DEFINE_int32(pcm16b_swb32, 95, "RTP payload type for PCM16b-swb32 (32 kHz)"); |
| static const bool pcm16b_swb32_dummy = |
| google::RegisterFlagValidator(&FLAGS_pcm16b_swb32, &ValidatePayloadType); |
| DEFINE_int32(pcm16b_swb48, 96, "RTP payload type for PCM16b-swb48 (48 kHz)"); |
| static const bool pcm16b_swb48_dummy = |
| google::RegisterFlagValidator(&FLAGS_pcm16b_swb48, &ValidatePayloadType); |
| DEFINE_int32(g722, 9, "RTP payload type for G.722"); |
| static const bool g722_dummy = |
| google::RegisterFlagValidator(&FLAGS_g722, &ValidatePayloadType); |
| DEFINE_int32(avt, 106, "RTP payload type for AVT/DTMF"); |
| static const bool avt_dummy = |
| google::RegisterFlagValidator(&FLAGS_avt, &ValidatePayloadType); |
| DEFINE_int32(red, 117, "RTP payload type for redundant audio (RED)"); |
| static const bool red_dummy = |
| google::RegisterFlagValidator(&FLAGS_red, &ValidatePayloadType); |
| DEFINE_int32(cn_nb, 13, "RTP payload type for comfort noise (8 kHz)"); |
| static const bool cn_nb_dummy = |
| google::RegisterFlagValidator(&FLAGS_cn_nb, &ValidatePayloadType); |
| DEFINE_int32(cn_wb, 98, "RTP payload type for comfort noise (16 kHz)"); |
| static const bool cn_wb_dummy = |
| google::RegisterFlagValidator(&FLAGS_cn_wb, &ValidatePayloadType); |
| DEFINE_int32(cn_swb32, 99, "RTP payload type for comfort noise (32 kHz)"); |
| static const bool cn_swb32_dummy = |
| google::RegisterFlagValidator(&FLAGS_cn_swb32, &ValidatePayloadType); |
| DEFINE_int32(cn_swb48, 100, "RTP payload type for comfort noise (48 kHz)"); |
| static const bool cn_swb48_dummy = |
| google::RegisterFlagValidator(&FLAGS_cn_swb48, &ValidatePayloadType); |
| DEFINE_bool(codec_map, false, "Prints the mapping between RTP payload type and " |
| "codec"); |
| DEFINE_string(replacement_audio_file, "", |
| "A PCM file that will be used to populate ""dummy"" RTP packets"); |
| DEFINE_string(ssrc, |
| "", |
| "Only use packets with this SSRC (decimal or hex, the latter " |
| "starting with 0x)"); |
| static const bool hex_ssrc_dummy = |
| google::RegisterFlagValidator(&FLAGS_ssrc, &ValidateSsrcValue); |
| |
| // Declaring helper functions (defined further down in this file). |
| std::string CodecName(webrtc::NetEqDecoder codec); |
| void RegisterPayloadTypes(NetEq* neteq); |
| void PrintCodecMapping(); |
| size_t ReplacePayload(webrtc::test::InputAudioFile* replacement_audio_file, |
| webrtc::scoped_ptr<int16_t[]>* replacement_audio, |
| webrtc::scoped_ptr<uint8_t[]>* payload, |
| size_t* payload_mem_size_bytes, |
| size_t* frame_size_samples, |
| WebRtcRTPHeader* rtp_header, |
| const webrtc::test::Packet* next_packet); |
| int CodecSampleRate(uint8_t payload_type); |
| int CodecTimestampRate(uint8_t payload_type); |
| bool IsComfortNosie(uint8_t payload_type); |
| |
| int main(int argc, char* argv[]) { |
| static const int kMaxChannels = 5; |
| static const int kMaxSamplesPerMs = 48000 / 1000; |
| static const int kOutputBlockSizeMs = 10; |
| |
| std::string program_name = argv[0]; |
| std::string usage = "Tool for decoding an RTP dump file using NetEq.\n" |
| "Run " + program_name + " --helpshort for usage.\n" |
| "Example usage:\n" + program_name + |
| " input.rtp output.pcm\n"; |
| google::SetUsageMessage(usage); |
| google::ParseCommandLineFlags(&argc, &argv, true); |
| |
| if (FLAGS_codec_map) { |
| PrintCodecMapping(); |
| } |
| |
| if (argc != 3) { |
| if (FLAGS_codec_map) { |
| // We have already printed the codec map. Just end the program. |
| return 0; |
| } |
| // Print usage information. |
| std::cout << google::ProgramUsage(); |
| return 0; |
| } |
| |
| printf("Input file: %s\n", argv[1]); |
| webrtc::scoped_ptr<webrtc::test::RtpFileSource> file_source( |
| webrtc::test::RtpFileSource::Create(argv[1])); |
| assert(file_source.get()); |
| |
| // Check if an SSRC value was provided. |
| if (!FLAGS_ssrc.empty()) { |
| uint32_t ssrc; |
| CHECK(ParseSsrc(FLAGS_ssrc, &ssrc)) << "Flag verification has failed."; |
| file_source->SelectSsrc(ssrc); |
| } |
| |
| FILE* out_file = fopen(argv[2], "wb"); |
| if (!out_file) { |
| std::cerr << "Cannot open output file " << argv[2] << std::endl; |
| exit(1); |
| } |
| std::cout << "Output file: " << argv[2] << std::endl; |
| |
| // Check if a replacement audio file was provided, and if so, open it. |
| bool replace_payload = false; |
| webrtc::scoped_ptr<webrtc::test::InputAudioFile> replacement_audio_file; |
| if (!FLAGS_replacement_audio_file.empty()) { |
| replacement_audio_file.reset( |
| new webrtc::test::InputAudioFile(FLAGS_replacement_audio_file)); |
| replace_payload = true; |
| } |
| |
| // Enable tracing. |
| webrtc::Trace::CreateTrace(); |
| webrtc::Trace::SetTraceFile((webrtc::test::OutputPath() + |
| "neteq_trace.txt").c_str()); |
| webrtc::Trace::set_level_filter(webrtc::kTraceAll); |
| |
| // Initialize NetEq instance. |
| int sample_rate_hz = 16000; |
| NetEq::Config config; |
| config.sample_rate_hz = sample_rate_hz; |
| NetEq* neteq = NetEq::Create(config); |
| RegisterPayloadTypes(neteq); |
| |
| // Read first packet. |
| webrtc::scoped_ptr<webrtc::test::Packet> packet(file_source->NextPacket()); |
| if (!packet) { |
| printf( |
| "Warning: input file is empty, or the filters did not match any " |
| "packets\n"); |
| webrtc::Trace::ReturnTrace(); |
| return 0; |
| } |
| bool packet_available = true; |
| |
| // Set up variables for audio replacement if needed. |
| webrtc::scoped_ptr<webrtc::test::Packet> next_packet; |
| bool next_packet_available = false; |
| size_t input_frame_size_timestamps = 0; |
| webrtc::scoped_ptr<int16_t[]> replacement_audio; |
| webrtc::scoped_ptr<uint8_t[]> payload; |
| size_t payload_mem_size_bytes = 0; |
| if (replace_payload) { |
| // Initially assume that the frame size is 30 ms at the initial sample rate. |
| // This value will be replaced with the correct one as soon as two |
| // consecutive packets are found. |
| input_frame_size_timestamps = 30 * sample_rate_hz / 1000; |
| replacement_audio.reset(new int16_t[input_frame_size_timestamps]); |
| payload_mem_size_bytes = 2 * input_frame_size_timestamps; |
| payload.reset(new uint8_t[payload_mem_size_bytes]); |
| next_packet.reset(file_source->NextPacket()); |
| assert(next_packet); |
| next_packet_available = true; |
| } |
| |
| // This is the main simulation loop. |
| // Set the simulation clock to start immediately with the first packet. |
| int time_now_ms = packet->time_ms(); |
| int next_input_time_ms = time_now_ms; |
| int next_output_time_ms = time_now_ms; |
| if (time_now_ms % kOutputBlockSizeMs != 0) { |
| // Make sure that next_output_time_ms is rounded up to the next multiple |
| // of kOutputBlockSizeMs. (Legacy bit-exactness.) |
| next_output_time_ms += |
| kOutputBlockSizeMs - time_now_ms % kOutputBlockSizeMs; |
| } |
| while (packet_available) { |
| // Check if it is time to insert packet. |
| while (time_now_ms >= next_input_time_ms && packet_available) { |
| assert(packet->virtual_payload_length_bytes() > 0); |
| // Parse RTP header. |
| WebRtcRTPHeader rtp_header; |
| packet->ConvertHeader(&rtp_header); |
| const uint8_t* payload_ptr = packet->payload(); |
| size_t payload_len = packet->payload_length_bytes(); |
| if (replace_payload) { |
| payload_len = ReplacePayload(replacement_audio_file.get(), |
| &replacement_audio, |
| &payload, |
| &payload_mem_size_bytes, |
| &input_frame_size_timestamps, |
| &rtp_header, |
| next_packet.get()); |
| payload_ptr = payload.get(); |
| } |
| int error = |
| neteq->InsertPacket(rtp_header, |
| payload_ptr, |
| payload_len, |
| packet->time_ms() * sample_rate_hz / 1000); |
| if (error != NetEq::kOK) { |
| if (neteq->LastError() == NetEq::kUnknownRtpPayloadType) { |
| std::cerr << "RTP Payload type " |
| << static_cast<int>(rtp_header.header.payloadType) |
| << " is unknown." << std::endl; |
| std::cerr << "Use --codec_map to view default mapping." << std::endl; |
| std::cerr << "Use --helpshort for information on how to make custom " |
| "mappings." << std::endl; |
| } else { |
| std::cerr << "InsertPacket returned error code " << neteq->LastError() |
| << std::endl; |
| std::cerr << "Header data:" << std::endl; |
| std::cerr << " PT = " |
| << static_cast<int>(rtp_header.header.payloadType) |
| << std::endl; |
| std::cerr << " SN = " << rtp_header.header.sequenceNumber |
| << std::endl; |
| std::cerr << " TS = " << rtp_header.header.timestamp << std::endl; |
| } |
| } |
| |
| // Get next packet from file. |
| webrtc::test::Packet* temp_packet = file_source->NextPacket(); |
| if (temp_packet) { |
| packet.reset(temp_packet); |
| } else { |
| packet_available = false; |
| } |
| if (replace_payload) { |
| // At this point |packet| contains the packet *after* |next_packet|. |
| // Swap Packet objects between |packet| and |next_packet|. |
| packet.swap(next_packet); |
| // Swap the status indicators unless they're already the same. |
| if (packet_available != next_packet_available) { |
| packet_available = !packet_available; |
| next_packet_available = !next_packet_available; |
| } |
| } |
| next_input_time_ms = packet->time_ms(); |
| } |
| |
| // Check if it is time to get output audio. |
| if (time_now_ms >= next_output_time_ms) { |
| static const int kOutDataLen = kOutputBlockSizeMs * kMaxSamplesPerMs * |
| kMaxChannels; |
| int16_t out_data[kOutDataLen]; |
| int num_channels; |
| int samples_per_channel; |
| int error = neteq->GetAudio(kOutDataLen, out_data, &samples_per_channel, |
| &num_channels, NULL); |
| if (error != NetEq::kOK) { |
| std::cerr << "GetAudio returned error code " << |
| neteq->LastError() << std::endl; |
| } else { |
| // Calculate sample rate from output size. |
| sample_rate_hz = 1000 * samples_per_channel / kOutputBlockSizeMs; |
| } |
| |
| // Write to file. |
| // TODO(hlundin): Make writing to file optional. |
| size_t write_len = samples_per_channel * num_channels; |
| if (fwrite(out_data, sizeof(out_data[0]), write_len, out_file) != |
| write_len) { |
| std::cerr << "Error while writing to file" << std::endl; |
| webrtc::Trace::ReturnTrace(); |
| exit(1); |
| } |
| next_output_time_ms += kOutputBlockSizeMs; |
| } |
| // Advance time to next event. |
| time_now_ms = std::min(next_input_time_ms, next_output_time_ms); |
| } |
| |
| std::cout << "Simulation done" << std::endl; |
| |
| fclose(out_file); |
| delete neteq; |
| webrtc::Trace::ReturnTrace(); |
| return 0; |
| } |
| |
| |
| // Help functions. |
| |
| // Maps a codec type to a printable name string. |
| std::string CodecName(webrtc::NetEqDecoder codec) { |
| switch (codec) { |
| case webrtc::kDecoderPCMu: |
| return "PCM-u"; |
| case webrtc::kDecoderPCMa: |
| return "PCM-a"; |
| case webrtc::kDecoderILBC: |
| return "iLBC"; |
| case webrtc::kDecoderISAC: |
| return "iSAC"; |
| case webrtc::kDecoderISACswb: |
| return "iSAC-swb (32 kHz)"; |
| case webrtc::kDecoderOpus: |
| return "Opus"; |
| case webrtc::kDecoderPCM16B: |
| return "PCM16b-nb (8 kHz)"; |
| case webrtc::kDecoderPCM16Bwb: |
| return "PCM16b-wb (16 kHz)"; |
| case webrtc::kDecoderPCM16Bswb32kHz: |
| return "PCM16b-swb32 (32 kHz)"; |
| case webrtc::kDecoderPCM16Bswb48kHz: |
| return "PCM16b-swb48 (48 kHz)"; |
| case webrtc::kDecoderG722: |
| return "G.722"; |
| case webrtc::kDecoderRED: |
| return "redundant audio (RED)"; |
| case webrtc::kDecoderAVT: |
| return "AVT/DTMF"; |
| case webrtc::kDecoderCNGnb: |
| return "comfort noise (8 kHz)"; |
| case webrtc::kDecoderCNGwb: |
| return "comfort noise (16 kHz)"; |
| case webrtc::kDecoderCNGswb32kHz: |
| return "comfort noise (32 kHz)"; |
| case webrtc::kDecoderCNGswb48kHz: |
| return "comfort noise (48 kHz)"; |
| default: |
| assert(false); |
| return "undefined"; |
| } |
| } |
| |
| // Registers all decoders in |neteq|. |
| void RegisterPayloadTypes(NetEq* neteq) { |
| assert(neteq); |
| int error; |
| error = neteq->RegisterPayloadType(webrtc::kDecoderPCMu, FLAGS_pcmu); |
| if (error) { |
| std::cerr << "Cannot register payload type " << FLAGS_pcmu << |
| " as " << CodecName(webrtc::kDecoderPCMu).c_str() << std::endl; |
| exit(1); |
| } |
| error = neteq->RegisterPayloadType(webrtc::kDecoderPCMa, FLAGS_pcma); |
| if (error) { |
| std::cerr << "Cannot register payload type " << FLAGS_pcma << |
| " as " << CodecName(webrtc::kDecoderPCMa).c_str() << std::endl; |
| exit(1); |
| } |
| error = neteq->RegisterPayloadType(webrtc::kDecoderILBC, FLAGS_ilbc); |
| if (error) { |
| std::cerr << "Cannot register payload type " << FLAGS_ilbc << |
| " as " << CodecName(webrtc::kDecoderILBC).c_str() << std::endl; |
| exit(1); |
| } |
| error = neteq->RegisterPayloadType(webrtc::kDecoderISAC, FLAGS_isac); |
| if (error) { |
| std::cerr << "Cannot register payload type " << FLAGS_isac << |
| " as " << CodecName(webrtc::kDecoderISAC).c_str() << std::endl; |
| exit(1); |
| } |
| error = neteq->RegisterPayloadType(webrtc::kDecoderISACswb, FLAGS_isac_swb); |
| if (error) { |
| std::cerr << "Cannot register payload type " << FLAGS_isac_swb << |
| " as " << CodecName(webrtc::kDecoderISACswb).c_str() << std::endl; |
| exit(1); |
| } |
| error = neteq->RegisterPayloadType(webrtc::kDecoderOpus, FLAGS_opus); |
| if (error) { |
| std::cerr << "Cannot register payload type " << FLAGS_opus << " as " |
| << CodecName(webrtc::kDecoderOpus).c_str() << std::endl; |
| exit(1); |
| } |
| error = neteq->RegisterPayloadType(webrtc::kDecoderPCM16B, FLAGS_pcm16b); |
| if (error) { |
| std::cerr << "Cannot register payload type " << FLAGS_pcm16b << |
| " as " << CodecName(webrtc::kDecoderPCM16B).c_str() << std::endl; |
| exit(1); |
| } |
| error = neteq->RegisterPayloadType(webrtc::kDecoderPCM16Bwb, |
| FLAGS_pcm16b_wb); |
| if (error) { |
| std::cerr << "Cannot register payload type " << FLAGS_pcm16b_wb << |
| " as " << CodecName(webrtc::kDecoderPCM16Bwb).c_str() << std::endl; |
| exit(1); |
| } |
| error = neteq->RegisterPayloadType(webrtc::kDecoderPCM16Bswb32kHz, |
| FLAGS_pcm16b_swb32); |
| if (error) { |
| std::cerr << "Cannot register payload type " << FLAGS_pcm16b_swb32 << |
| " as " << CodecName(webrtc::kDecoderPCM16Bswb32kHz).c_str() << |
| std::endl; |
| exit(1); |
| } |
| error = neteq->RegisterPayloadType(webrtc::kDecoderPCM16Bswb48kHz, |
| FLAGS_pcm16b_swb48); |
| if (error) { |
| std::cerr << "Cannot register payload type " << FLAGS_pcm16b_swb48 << |
| " as " << CodecName(webrtc::kDecoderPCM16Bswb48kHz).c_str() << |
| std::endl; |
| exit(1); |
| } |
| error = neteq->RegisterPayloadType(webrtc::kDecoderG722, FLAGS_g722); |
| if (error) { |
| std::cerr << "Cannot register payload type " << FLAGS_g722 << |
| " as " << CodecName(webrtc::kDecoderG722).c_str() << std::endl; |
| exit(1); |
| } |
| error = neteq->RegisterPayloadType(webrtc::kDecoderAVT, FLAGS_avt); |
| if (error) { |
| std::cerr << "Cannot register payload type " << FLAGS_avt << |
| " as " << CodecName(webrtc::kDecoderAVT).c_str() << std::endl; |
| exit(1); |
| } |
| error = neteq->RegisterPayloadType(webrtc::kDecoderRED, FLAGS_red); |
| if (error) { |
| std::cerr << "Cannot register payload type " << FLAGS_red << |
| " as " << CodecName(webrtc::kDecoderRED).c_str() << std::endl; |
| exit(1); |
| } |
| error = neteq->RegisterPayloadType(webrtc::kDecoderCNGnb, FLAGS_cn_nb); |
| if (error) { |
| std::cerr << "Cannot register payload type " << FLAGS_cn_nb << |
| " as " << CodecName(webrtc::kDecoderCNGnb).c_str() << std::endl; |
| exit(1); |
| } |
| error = neteq->RegisterPayloadType(webrtc::kDecoderCNGwb, FLAGS_cn_wb); |
| if (error) { |
| std::cerr << "Cannot register payload type " << FLAGS_cn_wb << |
| " as " << CodecName(webrtc::kDecoderCNGwb).c_str() << std::endl; |
| exit(1); |
| } |
| error = neteq->RegisterPayloadType(webrtc::kDecoderCNGswb32kHz, |
| FLAGS_cn_swb32); |
| if (error) { |
| std::cerr << "Cannot register payload type " << FLAGS_cn_swb32 << |
| " as " << CodecName(webrtc::kDecoderCNGswb32kHz).c_str() << std::endl; |
| exit(1); |
| } |
| error = neteq->RegisterPayloadType(webrtc::kDecoderCNGswb48kHz, |
| FLAGS_cn_swb48); |
| if (error) { |
| std::cerr << "Cannot register payload type " << FLAGS_cn_swb48 << |
| " as " << CodecName(webrtc::kDecoderCNGswb48kHz).c_str() << std::endl; |
| exit(1); |
| } |
| } |
| |
| void PrintCodecMapping() { |
| std::cout << CodecName(webrtc::kDecoderPCMu).c_str() << ": " << FLAGS_pcmu << |
| std::endl; |
| std::cout << CodecName(webrtc::kDecoderPCMa).c_str() << ": " << FLAGS_pcma << |
| std::endl; |
| std::cout << CodecName(webrtc::kDecoderILBC).c_str() << ": " << FLAGS_ilbc << |
| std::endl; |
| std::cout << CodecName(webrtc::kDecoderISAC).c_str() << ": " << FLAGS_isac << |
| std::endl; |
| std::cout << CodecName(webrtc::kDecoderISACswb).c_str() << ": " << |
| FLAGS_isac_swb << std::endl; |
| std::cout << CodecName(webrtc::kDecoderOpus).c_str() << ": " << FLAGS_opus |
| << std::endl; |
| std::cout << CodecName(webrtc::kDecoderPCM16B).c_str() << ": " << |
| FLAGS_pcm16b << std::endl; |
| std::cout << CodecName(webrtc::kDecoderPCM16Bwb).c_str() << ": " << |
| FLAGS_pcm16b_wb << std::endl; |
| std::cout << CodecName(webrtc::kDecoderPCM16Bswb32kHz).c_str() << ": " << |
| FLAGS_pcm16b_swb32 << std::endl; |
| std::cout << CodecName(webrtc::kDecoderPCM16Bswb48kHz).c_str() << ": " << |
| FLAGS_pcm16b_swb48 << std::endl; |
| std::cout << CodecName(webrtc::kDecoderG722).c_str() << ": " << FLAGS_g722 << |
| std::endl; |
| std::cout << CodecName(webrtc::kDecoderAVT).c_str() << ": " << FLAGS_avt << |
| std::endl; |
| std::cout << CodecName(webrtc::kDecoderRED).c_str() << ": " << FLAGS_red << |
| std::endl; |
| std::cout << CodecName(webrtc::kDecoderCNGnb).c_str() << ": " << |
| FLAGS_cn_nb << std::endl; |
| std::cout << CodecName(webrtc::kDecoderCNGwb).c_str() << ": " << |
| FLAGS_cn_wb << std::endl; |
| std::cout << CodecName(webrtc::kDecoderCNGswb32kHz).c_str() << ": " << |
| FLAGS_cn_swb32 << std::endl; |
| std::cout << CodecName(webrtc::kDecoderCNGswb48kHz).c_str() << ": " << |
| FLAGS_cn_swb48 << std::endl; |
| } |
| |
| size_t ReplacePayload(webrtc::test::InputAudioFile* replacement_audio_file, |
| webrtc::scoped_ptr<int16_t[]>* replacement_audio, |
| webrtc::scoped_ptr<uint8_t[]>* payload, |
| size_t* payload_mem_size_bytes, |
| size_t* frame_size_samples, |
| WebRtcRTPHeader* rtp_header, |
| const webrtc::test::Packet* next_packet) { |
| size_t payload_len = 0; |
| // Check for CNG. |
| if (IsComfortNosie(rtp_header->header.payloadType)) { |
| // If CNG, simply insert a zero-energy one-byte payload. |
| if (*payload_mem_size_bytes < 1) { |
| (*payload).reset(new uint8_t[1]); |
| *payload_mem_size_bytes = 1; |
| } |
| (*payload)[0] = 127; // Max attenuation of CNG. |
| payload_len = 1; |
| } else { |
| assert(next_packet->virtual_payload_length_bytes() > 0); |
| // Check if payload length has changed. |
| if (next_packet->header().sequenceNumber == |
| rtp_header->header.sequenceNumber + 1) { |
| if (*frame_size_samples != |
| next_packet->header().timestamp - rtp_header->header.timestamp) { |
| *frame_size_samples = |
| next_packet->header().timestamp - rtp_header->header.timestamp; |
| (*replacement_audio).reset( |
| new int16_t[*frame_size_samples]); |
| *payload_mem_size_bytes = 2 * *frame_size_samples; |
| (*payload).reset(new uint8_t[*payload_mem_size_bytes]); |
| } |
| } |
| // Get new speech. |
| assert((*replacement_audio).get()); |
| if (CodecTimestampRate(rtp_header->header.payloadType) != |
| CodecSampleRate(rtp_header->header.payloadType) || |
| rtp_header->header.payloadType == FLAGS_red || |
| rtp_header->header.payloadType == FLAGS_avt) { |
| // Some codecs have different sample and timestamp rates. And neither |
| // RED nor DTMF is supported for replacement. |
| std::cerr << "Codec not supported for audio replacement." << |
| std::endl; |
| webrtc::Trace::ReturnTrace(); |
| exit(1); |
| } |
| assert(*frame_size_samples > 0); |
| if (!replacement_audio_file->Read(*frame_size_samples, |
| (*replacement_audio).get())) { |
| std::cerr << "Could not read replacement audio file." << std::endl; |
| webrtc::Trace::ReturnTrace(); |
| exit(1); |
| } |
| // Encode it as PCM16. |
| assert((*payload).get()); |
| payload_len = WebRtcPcm16b_Encode((*replacement_audio).get(), |
| static_cast<int16_t>(*frame_size_samples), |
| (*payload).get()); |
| assert(payload_len == 2 * *frame_size_samples); |
| // Change payload type to PCM16. |
| switch (CodecSampleRate(rtp_header->header.payloadType)) { |
| case 8000: |
| rtp_header->header.payloadType = FLAGS_pcm16b; |
| break; |
| case 16000: |
| rtp_header->header.payloadType = FLAGS_pcm16b_wb; |
| break; |
| case 32000: |
| rtp_header->header.payloadType = FLAGS_pcm16b_swb32; |
| break; |
| case 48000: |
| rtp_header->header.payloadType = FLAGS_pcm16b_swb48; |
| break; |
| default: |
| std::cerr << "Payload type " << |
| static_cast<int>(rtp_header->header.payloadType) << |
| " not supported or unknown." << std::endl; |
| webrtc::Trace::ReturnTrace(); |
| exit(1); |
| } |
| } |
| return payload_len; |
| } |
| |
| int CodecSampleRate(uint8_t payload_type) { |
| if (payload_type == FLAGS_pcmu || |
| payload_type == FLAGS_pcma || |
| payload_type == FLAGS_ilbc || |
| payload_type == FLAGS_pcm16b || |
| payload_type == FLAGS_cn_nb) { |
| return 8000; |
| } else if (payload_type == FLAGS_isac || |
| payload_type == FLAGS_pcm16b_wb || |
| payload_type == FLAGS_g722 || |
| payload_type == FLAGS_cn_wb) { |
| return 16000; |
| } else if (payload_type == FLAGS_isac_swb || |
| payload_type == FLAGS_pcm16b_swb32 || |
| payload_type == FLAGS_cn_swb32) { |
| return 32000; |
| } else if (payload_type == FLAGS_opus || payload_type == FLAGS_pcm16b_swb48 || |
| payload_type == FLAGS_cn_swb48) { |
| return 48000; |
| } else if (payload_type == FLAGS_avt || |
| payload_type == FLAGS_red) { |
| return 0; |
| } else { |
| return -1; |
| } |
| } |
| |
| int CodecTimestampRate(uint8_t payload_type) { |
| if (payload_type == FLAGS_g722) { |
| return 8000; |
| } else { |
| return CodecSampleRate(payload_type); |
| } |
| } |
| |
| bool IsComfortNosie(uint8_t payload_type) { |
| if (payload_type == FLAGS_cn_nb || |
| payload_type == FLAGS_cn_wb || |
| payload_type == FLAGS_cn_swb32 || |
| payload_type == FLAGS_cn_swb48) { |
| return true; |
| } else { |
| return false; |
| } |
| } |