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> |
16 #include <limits.h> // For ULONG_MAX returned by strtoul. | 12 #include <limits.h> // For ULONG_MAX returned by strtoul. |
17 #include <stdio.h> | 13 #include <stdio.h> |
18 #include <stdlib.h> // For strtoul. | 14 #include <stdlib.h> // For strtoul. |
19 | 15 |
20 #include <algorithm> | 16 #include <algorithm> |
21 #include <iostream> | 17 #include <iostream> |
22 #include <memory> | 18 #include <memory> |
23 #include <limits> | |
24 #include <string> | 19 #include <string> |
25 | 20 |
26 #include "gflags/gflags.h" | 21 #include "gflags/gflags.h" |
27 #include "webrtc/base/checks.h" | 22 #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" | 23 #include "webrtc/modules/audio_coding/neteq/include/neteq.h" |
24 #include "webrtc/modules/audio_coding/neteq/tools/fake_decode_from_file.h" | |
32 #include "webrtc/modules/audio_coding/neteq/tools/input_audio_file.h" | 25 #include "webrtc/modules/audio_coding/neteq/tools/input_audio_file.h" |
26 #include "webrtc/modules/audio_coding/neteq/tools/neteq_packet_source_input.h" | |
27 #include "webrtc/modules/audio_coding/neteq/tools/neteq_replacement_input.h" | |
28 #include "webrtc/modules/audio_coding/neteq/tools/neteq_test.h" | |
33 #include "webrtc/modules/audio_coding/neteq/tools/output_audio_file.h" | 29 #include "webrtc/modules/audio_coding/neteq/tools/output_audio_file.h" |
34 #include "webrtc/modules/audio_coding/neteq/tools/output_wav_file.h" | 30 #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" | 31 #include "webrtc/modules/audio_coding/neteq/tools/rtp_file_source.h" |
38 #include "webrtc/modules/include/module_common_types.h" | 32 #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" | 33 #include "webrtc/test/testsupport/fileutils.h" |
42 #include "webrtc/typedefs.h" | 34 #include "webrtc/typedefs.h" |
43 | 35 |
44 namespace webrtc { | 36 namespace webrtc { |
45 namespace test { | 37 namespace test { |
46 namespace { | 38 namespace { |
47 | 39 |
48 // Parses the input string for a valid SSRC (at the start of the string). If a | 40 // 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 | 41 // valid SSRC is found, it is written to the output variable |ssrc|, and true is |
50 // returned. Otherwise, false is returned. | 42 // returned. Otherwise, false is returned. |
(...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
175 return "AVT/DTMF"; | 167 return "AVT/DTMF"; |
176 case NetEqDecoder::kDecoderCNGnb: | 168 case NetEqDecoder::kDecoderCNGnb: |
177 return "comfort noise (8 kHz)"; | 169 return "comfort noise (8 kHz)"; |
178 case NetEqDecoder::kDecoderCNGwb: | 170 case NetEqDecoder::kDecoderCNGwb: |
179 return "comfort noise (16 kHz)"; | 171 return "comfort noise (16 kHz)"; |
180 case NetEqDecoder::kDecoderCNGswb32kHz: | 172 case NetEqDecoder::kDecoderCNGswb32kHz: |
181 return "comfort noise (32 kHz)"; | 173 return "comfort noise (32 kHz)"; |
182 case NetEqDecoder::kDecoderCNGswb48kHz: | 174 case NetEqDecoder::kDecoderCNGswb48kHz: |
183 return "comfort noise (48 kHz)"; | 175 return "comfort noise (48 kHz)"; |
184 default: | 176 default: |
185 assert(false); | 177 FATAL(); |
186 return "undefined"; | 178 return "undefined"; |
187 } | 179 } |
188 } | 180 } |
189 | 181 |
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) { | 182 void PrintCodecMappingEntry(NetEqDecoder codec, google::int32 flag) { |
233 std::cout << CodecName(codec) << ": " << flag << std::endl; | 183 std::cout << CodecName(codec) << ": " << flag << std::endl; |
234 } | 184 } |
235 | 185 |
236 void PrintCodecMapping() { | 186 void PrintCodecMapping() { |
237 PrintCodecMappingEntry(NetEqDecoder::kDecoderPCMu, FLAGS_pcmu); | 187 PrintCodecMappingEntry(NetEqDecoder::kDecoderPCMu, FLAGS_pcmu); |
238 PrintCodecMappingEntry(NetEqDecoder::kDecoderPCMa, FLAGS_pcma); | 188 PrintCodecMappingEntry(NetEqDecoder::kDecoderPCMa, FLAGS_pcma); |
239 PrintCodecMappingEntry(NetEqDecoder::kDecoderILBC, FLAGS_ilbc); | 189 PrintCodecMappingEntry(NetEqDecoder::kDecoderILBC, FLAGS_ilbc); |
240 PrintCodecMappingEntry(NetEqDecoder::kDecoderISAC, FLAGS_isac); | 190 PrintCodecMappingEntry(NetEqDecoder::kDecoderISAC, FLAGS_isac); |
241 PrintCodecMappingEntry(NetEqDecoder::kDecoderISACswb, FLAGS_isac_swb); | 191 PrintCodecMappingEntry(NetEqDecoder::kDecoderISACswb, FLAGS_isac_swb); |
242 PrintCodecMappingEntry(NetEqDecoder::kDecoderOpus, FLAGS_opus); | 192 PrintCodecMappingEntry(NetEqDecoder::kDecoderOpus, FLAGS_opus); |
243 PrintCodecMappingEntry(NetEqDecoder::kDecoderPCM16B, FLAGS_pcm16b); | 193 PrintCodecMappingEntry(NetEqDecoder::kDecoderPCM16B, FLAGS_pcm16b); |
244 PrintCodecMappingEntry(NetEqDecoder::kDecoderPCM16Bwb, FLAGS_pcm16b_wb); | 194 PrintCodecMappingEntry(NetEqDecoder::kDecoderPCM16Bwb, FLAGS_pcm16b_wb); |
245 PrintCodecMappingEntry(NetEqDecoder::kDecoderPCM16Bswb32kHz, | 195 PrintCodecMappingEntry(NetEqDecoder::kDecoderPCM16Bswb32kHz, |
246 FLAGS_pcm16b_swb32); | 196 FLAGS_pcm16b_swb32); |
247 PrintCodecMappingEntry(NetEqDecoder::kDecoderPCM16Bswb48kHz, | 197 PrintCodecMappingEntry(NetEqDecoder::kDecoderPCM16Bswb48kHz, |
248 FLAGS_pcm16b_swb48); | 198 FLAGS_pcm16b_swb48); |
249 PrintCodecMappingEntry(NetEqDecoder::kDecoderG722, FLAGS_g722); | 199 PrintCodecMappingEntry(NetEqDecoder::kDecoderG722, FLAGS_g722); |
250 PrintCodecMappingEntry(NetEqDecoder::kDecoderAVT, FLAGS_avt); | 200 PrintCodecMappingEntry(NetEqDecoder::kDecoderAVT, FLAGS_avt); |
251 PrintCodecMappingEntry(NetEqDecoder::kDecoderRED, FLAGS_red); | 201 PrintCodecMappingEntry(NetEqDecoder::kDecoderRED, FLAGS_red); |
252 PrintCodecMappingEntry(NetEqDecoder::kDecoderCNGnb, FLAGS_cn_nb); | 202 PrintCodecMappingEntry(NetEqDecoder::kDecoderCNGnb, FLAGS_cn_nb); |
253 PrintCodecMappingEntry(NetEqDecoder::kDecoderCNGwb, FLAGS_cn_wb); | 203 PrintCodecMappingEntry(NetEqDecoder::kDecoderCNGwb, FLAGS_cn_wb); |
254 PrintCodecMappingEntry(NetEqDecoder::kDecoderCNGswb32kHz, FLAGS_cn_swb32); | 204 PrintCodecMappingEntry(NetEqDecoder::kDecoderCNGswb32kHz, FLAGS_cn_swb32); |
255 PrintCodecMappingEntry(NetEqDecoder::kDecoderCNGswb48kHz, FLAGS_cn_swb48); | 205 PrintCodecMappingEntry(NetEqDecoder::kDecoderCNGswb48kHz, FLAGS_cn_swb48); |
256 } | 206 } |
257 | 207 |
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) { | 208 int CodecSampleRate(uint8_t payload_type) { |
264 if (payload_type == FLAGS_pcmu || payload_type == FLAGS_pcma || | 209 if (payload_type == FLAGS_pcmu || payload_type == FLAGS_pcma || |
265 payload_type == FLAGS_ilbc || payload_type == FLAGS_pcm16b || | 210 payload_type == FLAGS_ilbc || payload_type == FLAGS_pcm16b || |
266 payload_type == FLAGS_cn_nb) | 211 payload_type == FLAGS_cn_nb) |
267 return 8000; | 212 return 8000; |
268 if (payload_type == FLAGS_isac || payload_type == FLAGS_pcm16b_wb || | 213 if (payload_type == FLAGS_isac || payload_type == FLAGS_pcm16b_wb || |
269 payload_type == FLAGS_g722 || payload_type == FLAGS_cn_wb) | 214 payload_type == FLAGS_g722 || payload_type == FLAGS_cn_wb) |
270 return 16000; | 215 return 16000; |
271 if (payload_type == FLAGS_isac_swb || payload_type == FLAGS_pcm16b_swb32 || | 216 if (payload_type == FLAGS_isac_swb || payload_type == FLAGS_pcm16b_swb32 || |
272 payload_type == FLAGS_cn_swb32) | 217 payload_type == FLAGS_cn_swb32) |
273 return 32000; | 218 return 32000; |
274 if (payload_type == FLAGS_opus || payload_type == FLAGS_pcm16b_swb48 || | 219 if (payload_type == FLAGS_opus || payload_type == FLAGS_pcm16b_swb48 || |
275 payload_type == FLAGS_cn_swb48) | 220 payload_type == FLAGS_cn_swb48) |
276 return 48000; | 221 return 48000; |
277 if (payload_type == FLAGS_avt || payload_type == FLAGS_red) | 222 if (payload_type == FLAGS_avt || payload_type == FLAGS_red) |
278 return 0; | 223 return 0; |
279 return -1; | 224 return -1; |
280 } | 225 } |
281 | 226 |
282 int CodecTimestampRate(uint8_t payload_type) { | 227 // 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); | 228 // outer layer on another NetEqInput object. |
284 } | 229 class FilterSsrcInput : public NetEqInput { |
230 public: | |
231 FilterSsrcInput(std::unique_ptr<NetEqInput> source, uint32_t ssrc) | |
232 : source_(std::move(source)), ssrc_(ssrc) { | |
233 FindNextWithCorrectSsrc(); | |
234 } | |
285 | 235 |
286 size_t ReplacePayload(InputAudioFile* replacement_audio_file, | 236 // All methods but GetPacket() simply relay to the |source_| object. |
287 std::unique_ptr<int16_t[]>* replacement_audio, | 237 rtc::Optional<int64_t> NextPacketTime() const override { |
288 std::unique_ptr<uint8_t[]>* payload, | 238 return source_->NextPacketTime(); |
ivoc
2016/06/14 16:39:57
Can't the next source packet be invalid based on S
hlundin-webrtc
2016/06/17 10:30:08
No. The FindNextWithCorrectSsrc() method will alwa
ivoc
2016/06/21 07:59:58
Ah right, good point.
| |
289 size_t* payload_mem_size_bytes, | 239 } |
290 size_t* frame_size_samples, | 240 rtc::Optional<int64_t> NextOutputEventTime() const override { |
291 WebRtcRTPHeader* rtp_header, | 241 return source_->NextOutputEventTime(); |
292 const Packet* next_packet) { | 242 } |
293 size_t payload_len = 0; | 243 |
294 // Check for CNG. | 244 // Returns the next packet, and throws away upcoming packets that do not match |
295 if (IsComfortNoise(rtp_header->header.payloadType)) { | 245 // the desired SSRC. |
296 // If CNG, simply insert a zero-energy one-byte payload. | 246 std::unique_ptr<PacketData> GetPacket() override { |
297 if (*payload_mem_size_bytes < 1) { | 247 std::unique_ptr<PacketData> packet_to_return = source_->GetPacket(); |
298 (*payload).reset(new uint8_t[1]); | 248 RTC_DCHECK(!packet_to_return || |
299 *payload_mem_size_bytes = 1; | 249 packet_to_return->header.header.ssrc == ssrc_); |
ivoc
2016/06/14 16:39:57
I don't understand this DCHECK, can you explain wh
hlundin-webrtc
2016/06/17 10:30:08
This holds because (1) only this method updates th
| |
300 } | 250 FindNextWithCorrectSsrc(); |
301 (*payload)[0] = 127; // Max attenuation of CNG. | 251 return packet_to_return; |
302 payload_len = 1; | 252 } |
303 } else { | 253 |
304 assert(next_packet->virtual_payload_length_bytes() > 0); | 254 void AdvanceOutputEvent() override { source_->AdvanceOutputEvent(); } |
305 // Check if payload length has changed. | 255 |
306 if (next_packet->header().sequenceNumber == | 256 bool ended() const override { return source_->ended(); } |
307 rtp_header->header.sequenceNumber + 1) { | 257 |
308 if (*frame_size_samples != | 258 rtc::Optional<RTPHeader> NextHeader() const override { |
309 next_packet->header().timestamp - rtp_header->header.timestamp) { | 259 return source_->NextHeader(); |
310 *frame_size_samples = | 260 } |
311 next_packet->header().timestamp - rtp_header->header.timestamp; | 261 |
312 (*replacement_audio).reset( | 262 private: |
313 new int16_t[*frame_size_samples]); | 263 void FindNextWithCorrectSsrc() { |
314 *payload_mem_size_bytes = 2 * *frame_size_samples; | 264 while (source_->NextHeader() && source_->NextHeader()->ssrc != ssrc_) { |
315 (*payload).reset(new uint8_t[*payload_mem_size_bytes]); | 265 source_->GetPacket(); |
316 } | |
317 } | |
318 // Get new speech. | |
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 } | 266 } |
367 } | 267 } |
368 return payload_len; | 268 |
369 } | 269 std::unique_ptr<NetEqInput> source_; |
270 uint32_t ssrc_; | |
271 }; | |
370 | 272 |
371 int RunTest(int argc, char* argv[]) { | 273 int RunTest(int argc, char* argv[]) { |
372 static const int kOutputBlockSizeMs = 10; | |
373 | |
374 std::string program_name = argv[0]; | 274 std::string program_name = argv[0]; |
375 std::string usage = "Tool for decoding an RTP dump file using NetEq.\n" | 275 std::string usage = "Tool for decoding an RTP dump file using NetEq.\n" |
376 "Run " + program_name + " --helpshort for usage.\n" | 276 "Run " + program_name + " --helpshort for usage.\n" |
377 "Example usage:\n" + program_name + | 277 "Example usage:\n" + program_name + |
378 " input.rtp output.{pcm, wav}\n"; | 278 " input.rtp output.{pcm, wav}\n"; |
379 google::SetUsageMessage(usage); | 279 google::SetUsageMessage(usage); |
380 google::ParseCommandLineFlags(&argc, &argv, true); | 280 google::ParseCommandLineFlags(&argc, &argv, true); |
381 | 281 |
382 if (FLAGS_codec_map) { | 282 if (FLAGS_codec_map) { |
383 PrintCodecMapping(); | 283 PrintCodecMapping(); |
384 } | 284 } |
385 | 285 |
386 if (argc != 3) { | 286 if (argc != 3) { |
387 if (FLAGS_codec_map) { | 287 if (FLAGS_codec_map) { |
388 // We have already printed the codec map. Just end the program. | 288 // We have already printed the codec map. Just end the program. |
389 return 0; | 289 return 0; |
390 } | 290 } |
391 // Print usage information. | 291 // Print usage information. |
392 std::cout << google::ProgramUsage(); | 292 std::cout << google::ProgramUsage(); |
393 return 0; | 293 return 0; |
394 } | 294 } |
395 | 295 |
396 printf("Input file: %s\n", argv[1]); | 296 const std::string input_file_name = argv[1]; |
397 | 297 std::unique_ptr<NetEqInput> input; |
398 bool is_rtp_dump = false; | 298 if (RtpFileSource::ValidRtpDump(input_file_name) || |
399 std::unique_ptr<PacketSource> file_source; | 299 RtpFileSource::ValidPcap(input_file_name)) { |
400 RtcEventLogSource* event_log_source = nullptr; | 300 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 { | 301 } else { |
406 event_log_source = RtcEventLogSource::Create(argv[1]); | 302 input.reset(new NetEqEventLogInput(input_file_name)); |
407 file_source.reset(event_log_source); | |
408 } | 303 } |
409 | 304 |
410 assert(file_source.get()); | 305 std::cout << "Input file: " << input_file_name << std::endl; |
306 RTC_CHECK(input) << "Cannot open input file"; | |
307 RTC_CHECK(!input->ended()) << "Input file is empty"; | |
411 | 308 |
412 // Check if an SSRC value was provided. | 309 // Check if an SSRC value was provided. |
413 if (!FLAGS_ssrc.empty()) { | 310 if (!FLAGS_ssrc.empty()) { |
414 uint32_t ssrc; | 311 uint32_t ssrc; |
415 RTC_CHECK(ParseSsrc(FLAGS_ssrc, &ssrc)) << "Flag verification has failed."; | 312 RTC_CHECK(ParseSsrc(FLAGS_ssrc, &ssrc)) << "Flag verification has failed."; |
416 file_source->SelectSsrc(ssrc); | 313 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 } | 314 } |
443 | 315 |
444 // Check the sample rate. | 316 // Check the sample rate. |
445 int sample_rate_hz = CodecSampleRate(packet->header().payloadType); | 317 rtc::Optional<RTPHeader> first_rtp_header = input->NextHeader(); |
446 if (sample_rate_hz <= 0) { | 318 RTC_CHECK(first_rtp_header); |
447 printf("Warning: Invalid sample rate from RTP packet.\n"); | 319 const int sample_rate_hz = CodecSampleRate(first_rtp_header->payloadType); |
448 Trace::ReturnTrace(); | 320 RTC_CHECK_GT(sample_rate_hz, 0); |
449 return 0; | |
450 } | |
451 | 321 |
452 // Open the output file now that we know the sample rate. (Rate is only needed | 322 // Open the output file now that we know the sample rate. (Rate is only needed |
453 // for wav files.) | 323 // for wav files.) |
454 // Check output file type. | 324 const std::string output_file_name = argv[2]; |
455 std::string output_file_name = argv[2]; | |
456 std::unique_ptr<AudioSink> output; | 325 std::unique_ptr<AudioSink> output; |
457 if (output_file_name.size() >= 4 && | 326 if (output_file_name.size() >= 4 && |
458 output_file_name.substr(output_file_name.size() - 4) == ".wav") { | 327 output_file_name.substr(output_file_name.size() - 4) == ".wav") { |
459 // Open a wav file. | 328 // Open a wav file. |
460 output.reset(new OutputWavFile(output_file_name, sample_rate_hz)); | 329 output.reset(new OutputWavFile(output_file_name, sample_rate_hz)); |
461 } else { | 330 } else { |
462 // Open a pcm file. | 331 // Open a pcm file. |
463 output.reset(new OutputAudioFile(output_file_name)); | 332 output.reset(new OutputAudioFile(output_file_name)); |
464 } | 333 } |
465 | 334 |
466 std::cout << "Output file: " << argv[2] << std::endl; | 335 std::cout << "Output file: " << output_file_name << std::endl; |
467 | 336 |
468 // Enable tracing. | 337 NetEqTest::DecoderMap codecs; |
469 Trace::CreateTrace(); | 338 codecs[FLAGS_pcmu] = std::make_pair(NetEqDecoder::kDecoderPCMu, "pcmu"); |
ivoc
2016/06/14 16:39:57
Since DecoderMap is a std::map, it should be possi
hlundin-webrtc
2016/06/17 10:30:09
Done.
| |
470 Trace::SetTraceFile((OutputPath() + "neteq_trace.txt").c_str()); | 339 codecs[FLAGS_pcma] = std::make_pair(NetEqDecoder::kDecoderPCMa, "pcma"); |
471 Trace::set_level_filter(kTraceAll); | 340 codecs[FLAGS_ilbc] = std::make_pair(NetEqDecoder::kDecoderILBC, "ilbc"); |
341 codecs[FLAGS_isac] = std::make_pair(NetEqDecoder::kDecoderISAC, "isac"); | |
342 codecs[FLAGS_isac_swb] = | |
343 std::make_pair(NetEqDecoder::kDecoderISACswb, "isac-swb"); | |
344 codecs[FLAGS_opus] = std::make_pair(NetEqDecoder::kDecoderOpus, "opus"); | |
345 codecs[FLAGS_pcm16b] = | |
346 std::make_pair(NetEqDecoder::kDecoderPCM16B, "pcm16-nb"); | |
347 codecs[FLAGS_pcm16b_wb] = | |
348 std::make_pair(NetEqDecoder::kDecoderPCM16Bwb, "pcm16-wb"); | |
349 codecs[FLAGS_pcm16b_swb32] = | |
350 std::make_pair(NetEqDecoder::kDecoderPCM16Bswb32kHz, "pcm16-swb32"); | |
351 codecs[FLAGS_pcm16b_swb48] = | |
352 std::make_pair(NetEqDecoder::kDecoderPCM16Bswb48kHz, "pcm16-swb48"); | |
353 codecs[FLAGS_g722] = std::make_pair(NetEqDecoder::kDecoderG722, "g722"); | |
354 codecs[FLAGS_avt] = std::make_pair(NetEqDecoder::kDecoderAVT, "avt"); | |
355 codecs[FLAGS_red] = std::make_pair(NetEqDecoder::kDecoderRED, "red"); | |
356 codecs[FLAGS_cn_nb] = std::make_pair(NetEqDecoder::kDecoderCNGnb, "cng-nb"); | |
357 codecs[FLAGS_cn_wb] = std::make_pair(NetEqDecoder::kDecoderCNGwb, "cng-wb"); | |
358 codecs[FLAGS_cn_swb32] = | |
359 std::make_pair(NetEqDecoder::kDecoderCNGswb32kHz, "cng-swb32"); | |
360 codecs[FLAGS_cn_swb48] = | |
361 std::make_pair(NetEqDecoder::kDecoderCNGswb48kHz, "cng-swb48"); | |
472 | 362 |
473 // Initialize NetEq instance. | 363 // Check if a replacement audio file was provided. |
364 std::unique_ptr<AudioDecoder> replacement_decoder; | |
365 NetEqTest::ExtDecoderMap ext_codecs; | |
366 if (!FLAGS_replacement_audio_file.empty()) { | |
367 // Find largest unused payload type. | |
368 int replacement_pt = 127; | |
369 while (codecs.find(replacement_pt) != codecs.end() || | |
370 ext_codecs.find(replacement_pt) != ext_codecs.end()) { | |
ivoc
2016/06/14 16:39:57
To me this format looks easier to understand, but
hlundin-webrtc
2016/06/17 10:30:09
Done.
| |
371 --replacement_pt; | |
372 RTC_CHECK_GE(replacement_pt, 0); | |
373 } | |
374 std::set<uint8_t> cn_types; | |
375 cn_types.insert(FLAGS_cn_nb); | |
ivoc
2016/06/14 16:39:56
Initializer list would be nice here, i.e. cn_types
hlundin-webrtc
2016/06/17 10:30:09
Done.
| |
376 cn_types.insert(FLAGS_cn_wb); | |
377 cn_types.insert(FLAGS_cn_swb32); | |
378 cn_types.insert(FLAGS_cn_swb48); | |
379 std::set<uint8_t> forbidden_types; | |
ivoc
2016/06/14 16:39:56
And here.
hlundin-webrtc
2016/06/17 10:30:09
Done.
| |
380 forbidden_types.insert(FLAGS_g722); | |
381 forbidden_types.insert(FLAGS_red); | |
382 forbidden_types.insert(FLAGS_avt); | |
383 input.reset(new NetEqReplacementInput(std::move(input), replacement_pt, | |
384 cn_types, forbidden_types)); | |
385 | |
386 replacement_decoder.reset(new FakeDecodeFromFile( | |
387 std::unique_ptr<InputAudioFile>( | |
388 new InputAudioFile(FLAGS_replacement_audio_file)), | |
389 false)); | |
390 NetEqTest::ExternalDecoderInfo ext_dec_info = { | |
391 replacement_decoder.get(), NetEqDecoder::kDecoderArbitrary, | |
392 "replacement codec", 48000}; | |
393 ext_codecs[replacement_pt] = ext_dec_info; | |
394 } | |
395 | |
396 DefaultNetEqTestErrorCallback error_cb; | |
474 NetEq::Config config; | 397 NetEq::Config config; |
475 config.sample_rate_hz = sample_rate_hz; | 398 config.sample_rate_hz = sample_rate_hz; |
476 NetEq* neteq = | 399 NetEqTest test(config, codecs, ext_codecs, std::move(input), |
477 NetEq::Create(config, CreateBuiltinAudioDecoderFactory()); | 400 std::move(output), &error_cb); |
478 RegisterPayloadTypes(neteq); | |
479 | 401 |
402 int64_t test_duration_ms = test.Run(); | |
403 NetEqNetworkStatistics stats = test.SimulationStats(); | |
480 | 404 |
481 // Set up variables for audio replacement if needed. | 405 printf("Simulation statistics:\n"); |
482 std::unique_ptr<Packet> next_packet; | 406 printf(" output duration: %li ms\n", test_duration_ms); |
483 bool next_packet_available = false; | 407 printf(" packet_loss_rate: %f %%\n", |
484 size_t input_frame_size_timestamps = 0; | 408 100.0 * stats.packet_loss_rate / 16384.0); |
485 std::unique_ptr<int16_t[]> replacement_audio; | 409 printf(" packet_discard_rate: %f %%\n", |
486 std::unique_ptr<uint8_t[]> payload; | 410 100.0 * stats.packet_discard_rate / 16384.0); |
487 size_t payload_mem_size_bytes = 0; | 411 printf(" expand_rate: %f %%\n", 100.0 * stats.expand_rate / 16384.0); |
488 if (replace_payload) { | 412 printf(" speech_expand_rate: %f %%\n", |
489 // Initially assume that the frame size is 30 ms at the initial sample rate. | 413 100.0 * stats.speech_expand_rate / 16384.0); |
490 // This value will be replaced with the correct one as soon as two | 414 printf(" preemptive_rate: %f %%\n", 100.0 * stats.preemptive_rate / 16384.0); |
491 // consecutive packets are found. | 415 printf(" accelerate_rate: %f %%\n", 100.0 * stats.accelerate_rate / 16384.0); |
492 input_frame_size_timestamps = 30 * sample_rate_hz / 1000; | 416 printf(" secondary_decoded_rate: %f %%\n", |
493 replacement_audio.reset(new int16_t[input_frame_size_timestamps]); | 417 100.0 * stats.secondary_decoded_rate / 16384.0); |
494 payload_mem_size_bytes = 2 * input_frame_size_timestamps; | 418 printf(" clockdrift_ppm: %d ppm\n", stats.clockdrift_ppm); |
495 payload.reset(new uint8_t[payload_mem_size_bytes]); | 419 printf(" mean_waiting_time_ms: %d ms\n", stats.mean_waiting_time_ms); |
496 next_packet = file_source->NextPacket(); | 420 printf(" median_waiting_time_ms: %d ms\n", stats.median_waiting_time_ms); |
497 assert(next_packet); | 421 printf(" min_waiting_time_ms: %d ms\n", stats.min_waiting_time_ms); |
498 next_packet_available = true; | 422 printf(" max_waiting_time_ms: %d ms\n", stats.max_waiting_time_ms); |
499 } | |
500 | 423 |
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; | 424 return 0; |
631 } | 425 } |
632 | 426 |
633 } // namespace | 427 } // namespace |
634 } // namespace test | 428 } // namespace test |
635 } // namespace webrtc | 429 } // namespace webrtc |
636 | 430 |
637 int main(int argc, char* argv[]) { | 431 int main(int argc, char* argv[]) { |
638 webrtc::test::RunTest(argc, argv); | 432 webrtc::test::RunTest(argc, argv); |
639 } | 433 } |
OLD | NEW |