Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(249)

Side by Side Diff: webrtc/modules/audio_coding/neteq/tools/neteq_rtpplay.cc

Issue 2020363003: Refactor neteq_rtpplay (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: Change how SSRC filtering works Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698